Overview

This notebook contains the analysis of Hypothesis 4, as preregistered at https://osf.io/vwg6u/:

The absolute distance between intermediate estimates and the final estimate of the rate of forgetting for a particular learner/fact combination during a learning session will converge on zero (i.e., fall within a specified boundary above zero) within fewer adjustments when the initial estimate of the rate of forgetting was based on the most predictive combination(s) of learner and/or fact history than when the initial estimate of the rate of forgetting was set at the default value of 0.3.

Setup

Load packages

library(BayesFactor)
library(dplyr)
library(forcats)
library(ggplot2)
library(purrr)
library(readr)
library(stringr)
library(tidyr)
library(tikzDevice)
library(lme4)
library(lmerTest)

Attaching package: ‘lmerTest’

The following object is masked from ‘package:lme4’:

    lmer

The following object is masked from ‘package:stats’:

    step
library(fst)
theme_set(theme_light(base_size = 14) +
  theme(strip.text = element_text(colour = "black")))
knitr::opts_chunk$set(fig.width=16, fig.height=16) 

Load data

responses_lab_2_with_model_params <- read.fst(file.path("..", "data", "processed", "lab", "session2", "session2_rl2_with_alpha_lab.fst"))
responses_mturk_2_with_model_params <- read.fst(file.path("..", "data", "processed", "mturk", "session2", "session2_rl2_with_alpha_mturk.fst"))
fix_condition_labels <- function(x) {
  rowwise(x) %>%
  mutate(condition = str_replace(condition, "-and-", " & ") %>%
           str_replace("student", "learner") %>%
           str_to_title()) %>%
  ungroup() %>%
  mutate(condition = fct_relevel(condition, "Fact & Learner", after = Inf)) # Move F&L level to the end
}
block2_with_model_params <- bind_rows(mutate(responses_lab_2_with_model_params, dataset = "Lab"),
                                      mutate(responses_mturk_2_with_model_params, dataset = "MTurk")) %>%
  fix_condition_labels()

Subset data

The preregistration specified 22 April 2019 as the cutoff date for data collection. We reached the minimum of 25 participants per condition in the lab sample before this date, but not in the Mechanical Turk sample. For that reason data collection on MTurk continued until there were at least 25 participants per condition (14 May 2019) and was stopped only then. The analysis reported in the paper is based only on the data from before the cutoff date, but this notebook also reports the same analysis done on the full dataset.

subjects_cutoff <- read_csv(file.path("..", "data", "processed", "subjects_before_cutoff.csv"))
Parsed with column specification:
cols(
  subject = col_character()
)
block_2_full <- block2_with_model_params 
block_2_cutoff <- block2_with_model_params %>%
  right_join(subjects_cutoff, by = "subject")

Convergence of the estimate

Each trial has a value alpha that expresses the estimated rate of forgetting of the fact at the start of the trial. The rate of forgetting estimate starts at the predicted value (which depends on the condition). It is then updated after each repetition of the fact to better match the observed responses.

alpha_change <- block_2_full %>%
  group_by(dataset, subject, condition, fact_id) %>%
  arrange(subject, condition, fact_id, repetition) %>%
  mutate(d_alpha = alpha - lag(alpha)) %>%
  mutate(d_alpha = ifelse(is.na(d_alpha), 0, d_alpha)) %>%
  mutate(abs_d_alpha = abs(d_alpha)) %>%
  mutate(final_alpha = tail(alpha,1)) %>%
  ungroup() %>%
  select(dataset, subject, condition, fact_id, repetition, alpha, d_alpha, abs_d_alpha, final_alpha)

The plot below visualises the development of each alpha estimate (every learner/fact combination is represented by a line). It shows several things:

  • The initial rate of forgetting estimate differs depending on the condition: in conditions with a domain-wide prediction (Default and Domain) the same value is always used, while in conditions with more individualised predictions (Fact and/or Learner) the starting value differs between facts and/or learners.
  • The rate of forgetting estimate is only changed after the third presentation of the fact. Up to that point it is always a horizontal line.
  • Because of the way the scheduling algorithm works, there is invariably a plume towards the top right: facts with a lower rate of forgetting are repeated less frequently than facts with a higher rate of forgetting.
ggplot(alpha_change, aes(x = repetition, y = alpha, group = interaction(subject, fact_id))) +
  facet_grid(dataset ~ condition, labeller = label_wrap_gen()) +
  geom_line(alpha = 0.1) +
  labs(x = "Presentation",
       y = "Rate of forgetting")

Our question is whether the model can find the correct rate of forgetting more quickly in conditions that use a predicted value as their starting point.

The plot below shows the change in the rate of forgetting estimate compared to the previous presentation. Note that changes are capped at 0.0496 in both directions. It appears that many of the changes are as large as possible.

ggplot(alpha_change, aes(x = repetition, y = d_alpha, group = interaction(subject, fact_id))) +
  facet_grid(dataset ~ condition, labeller = label_wrap_gen()) +
  geom_line(alpha = 0.1) +
  labs(x = "Presentation",
       y = "Change in rate of forgetting")

The distribution of changes (shown below, excluding the first three presentations since they can never have changes) confirms that many changes are either as large as possible, or almost zero. The dotted horizontal lines show the boundaries of what we consider to be the convergence zone (0.00496 on either side of zero). This plot also gives an indication of the balance between upward and downward changes. It is especially apparent that changes in the Default condition are biased towards upward changes, which makes sense given the general diffulty of the material. It looks like the changes may be more balanced in other conditions.

alpha_change %>%
  filter(repetition > 3) %>%
ggplot(aes(x = condition, y = d_alpha)) +
  facet_grid(dataset ~ ., labeller = label_wrap_gen()) +
  geom_jitter(aes(colour = condition), width = 0.1, height = 0, alpha = 0.1) +
  geom_violin(fill = NA) +
  geom_hline(yintercept = c(0.00496, -0.00496), lty = 2) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change in rate of forgetting",
       caption = "Excluding the first 3 trials, in which RoF cannot change.")

alpha_change %>%
  filter(repetition > 3) %>%
ggplot(aes(x = d_alpha)) +
  facet_grid(dataset ~ condition, labeller = label_wrap_gen()) +
  geom_histogram(aes(fill = abs_d_alpha <= 0.00496), binwidth = 0.002) +
  labs(x = "Change in rate of forgetting",
       fill = "Within\nconvergence\nzone",
       caption = "Excluding the first 3 trials, in which RoF cannot change.")

The number of presentations per fact, which we expected to drop with better prediction, in fact seems to be higher in predictive conditions, if anything:

n_reps <- block_2_full %>%
  group_by(dataset, subject, condition, fact_id) %>%
  summarise(repetitions = max(repetition))
ggplot(filter(n_reps, repetitions >= 4), aes(x = condition, y = repetitions)) +
  facet_grid(dataset ~ .) +
  geom_violin() +
  geom_jitter(aes(colour = condition), width = 0.05, height = 0, alpha = 0.5) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Repetitions",
       caption = "Excluding facts with fewer than 4 repetitions")

This might simply be an effect of the high difficulty of items overall: in most cases, if the rate of forgetting is estimated more accurately from the start, the item should be repeated more often since it is correctly assessed as more difficult. We would expect that for the easiest facts (of which there are only a few), prediction does decrease repetitions compared to the default condition.

As a sanity check, verify that the number of repetitions scales with the final alpha estimate:

n_reps_by_alpha <- block_2_full %>%
  group_by(dataset, subject, condition, fact_id) %>%
  summarise(repetitions = max(repetition),
            alpha = alpha[which.max(repetition)])
ggplot(filter(n_reps_by_alpha, repetitions >= 4), aes(x = alpha, y = repetitions, colour = condition)) +
  facet_grid(dataset ~ condition, labeller = label_wrap_gen()) +
  geom_jitter(alpha = 0.5) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE)

The plot below shows only the number of repetitions for cases in which the final alpha is lower than 0.3, and hints at an improvement (particularly in the MTurk data). Starting off with a lower-frequency repetition schedule indeed seems to lead to fewer repetitions overall.

n_reps_by_alpha <- block_2_full %>%
  group_by(dataset, subject, condition, fact_id) %>%
  summarise(repetitions = max(repetition),
            alpha = alpha[which.max(repetition)])
ggplot(filter(n_reps_by_alpha, repetitions >= 4, alpha < 0.3), aes(x = condition, y = repetitions, colour = condition)) +
  facet_grid(dataset ~ .) +
  geom_violin() +
  geom_jitter(width = 0.05, height = 0, alpha = 0.5) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Repetitions",
       caption = "Only showing facts with alpha < 0.3; excluding facts with fewer than 4 repetitions")

Indeed, an exploratory Bayesian ANOVA shows strong evidence for an effect of condition on the number of repetitions among low-alpha items (but no effect of dataset):

n_reps_by_alpha_low <- n_reps_by_alpha %>%
  ungroup() %>%
  mutate_if(is_character, as.factor) %>%
  filter(repetitions >= 4, alpha < 0.3)
anovaBF(
  repetitions ~ condition * dataset,
  data = n_reps_by_alpha_low,
  progress = FALSE
)
data coerced from tibble to data frame
Bayes factor analysis
--------------
[1] dataset                                 : 0.5233318 ±0%
[2] condition                               : 142.7986  ±0%
[3] dataset + condition                     : 35.61571  ±1.36%
[4] dataset + condition + dataset:condition : 83738.58  ±1.5%

Against denominator:
  Intercept only 
---
Bayes factor type: BFlinearModel, JZS

Follow-up t-tests indicate that adapation in general has a beneficial effect...

ttestBF(filter(n_reps_by_alpha_low, condition == "Default")$repetitions, filter(n_reps_by_alpha_low, condition != "Default")$repetitions)
Bayes factor analysis
--------------
[1] Alt., r=0.707 : 159.0542 ±0%

Against denominator:
  Null, mu1-mu2 = 0 
---
Bayes factor type: BFindepSample, JZS

... but that there is only anecdotal evidence supporting a more individualised adaptation over a domain-wide adaptation:

ttestBF(filter(n_reps_by_alpha_low, condition == "Domain")$repetitions, filter(n_reps_by_alpha_low, condition %in% c("Fact & Learner", "Fact", "Learner"))$repetitions)
Bayes factor analysis
--------------
[1] Alt., r=0.707 : 2.047736 ±0%

Against denominator:
  Null, mu1-mu2 = 0 
---
Bayes factor type: BFindepSample, JZS

If we plot the cumulative percentage of converged estimates, we see that estimates in the default condition are more likely to converge than those in other conditions---not what we expected.

# convergence <- alpha_change %>%
#   group_by(dataset, subject, condition, fact_id) %>%
#   filter(abs_d_alpha > 0.00496) %>% # Keep cases in which the adjustment is outside the window
#   summarise(convergence_point = max(repetition) + 1) %>%
#   ungroup() %>%
#   mutate(dataset = as.factor(dataset),
#          subject = as.factor(subject),
#          condition = as.factor(condition),
#          fact_id = as.factor(fact_id))
# convergence <- alpha_change %>%
#   group_by(dataset, subject, condition, fact_id) %>%
#   mutate(within_boundary = abs_d_alpha <= 0.00496) %>%
#   mutate(total_reps = max(repetition)) %>%
#   filter(!within_boundary) %>%
#   summarise(total_reps = total_reps[1], convergence_point = max(repetition) + 1, final_alpha = alpha[which.max(repetition)]) %>%
#   mutate(convergence_point = ifelse(convergence_point > total_reps, NA, convergence_point))
convergence <- alpha_change %>%
  group_by(dataset, subject, condition, fact_id) %>%
  filter(max(repetition) > 3) %>%
  mutate(within_boundary = abs_d_alpha <= 0.00496) %>%
  mutate(total_reps = max(repetition)) %>%
  summarise(total_reps = total_reps[1],
            convergence_point = last(repetition[!within_boundary]) + 1) %>%
  mutate(convergence_point = ifelse(convergence_point > total_reps, NA, convergence_point))
convergence_cumulative <- convergence %>%
  group_by(dataset, condition) %>%
  count(convergence_point) %>%
  complete(nesting(dataset, condition), convergence_point = c(0:max(convergence$convergence_point, na.rm = T), NA), fill = list(n = 0)) %>%
  mutate(perc_converged = n / sum(n)) %>%
  mutate(perc_converged = cumsum(perc_converged)) %>%
  ungroup() %>%
  arrange(dataset, condition, convergence_point) %>%
  fill(perc_converged)
  
convergence_cumulative_combined <- convergence %>%
  group_by(condition) %>%
  count(convergence_point) %>%
  complete(nesting(condition), convergence_point = c(0:max(convergence$convergence_point, na.rm = T), NA), fill = list(n = 0)) %>%
  mutate(perc_converged = n / sum(n)) %>%
  mutate(perc_converged = cumsum(perc_converged)) %>%
  ungroup() %>%
  arrange(condition, convergence_point) %>%
  fill(perc_converged)
convergence_cumulative_combined %>%
  filter(!is.na(convergence_point)) %>%
  ggplot(aes(x = convergence_point, y = perc_converged, colour = condition, lty = condition)) +
  # facet_wrap(~ dataset) +
  geom_line(size = 1) +
  scale_colour_brewer(type = "qual", palette = 7) +
  scale_y_continuous(labels = scales::percent_format()) +
  expand_limits(x = 0, y = 0) +
  labs(x = "Repetition",
       y = "Estimates converged",
       colour = "Condition",
       lty = "Condition")

       # caption = "Convergence means that there are no more adjustments larger than 0.00496")
ggsave("../output/cum_perc_converged.pdf", device = "pdf", width = 5, height = 3)

Make the plot shown in the paper:

convergence_cumulative_combined_tex <- convergence_cumulative_combined
levels(convergence_cumulative_combined_tex$condition)[5] <- "Fact \\& Learner"
tikz(file = "../output/cum_perc_converged.tex", width = 4.75, height = 2)
convergence_cumulative_combined_tex %>%
  filter(!is.na(convergence_point)) %>%
  ggplot(aes(x = convergence_point, y = perc_converged, colour = condition, lty = condition)) +
  geom_line(size = 1) +
  scale_colour_brewer(type = "qual", palette = 7) +
  scale_y_continuous(labels = scales::percent_format(suffix = "\\%")) +
  expand_limits(x = 0, y = 0) +
  labs(x = "Repetition",
       y = "Estimates converged",
       colour = "Condition",
       lty = "Condition") +
  theme_light()
dev.off()
null device 
          1 

What proportion of estimates converged in each condition?

convergence_cumulative %>%
  group_by(dataset, condition) %>%
  filter(!is.na(convergence_point)) %>%
  summarise(n_converged = sum(n),
            perc_converged = max(perc_converged))

For the estimates that did converge, plot the distribution of convergence points by condition:

convergence %>%
  filter(!is.na(convergence_point)) %>%
  ggplot(aes(x = condition, y = convergence_point)) +
  # facet_grid(dataset ~ ., labeller = label_wrap_gen()) +
  geom_jitter(aes(colour = condition), width = 0.1, height = 0, alpha = 0.1) +
  geom_violin(fill = NA) +
  expand_limits(y = 0) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Convergence point")
ggsave("../output/convergence_point.pdf", device = "pdf", width = 5, height = 3)

Make the plot shown in the paper:

convergence_tex <- convergence
levels(convergence_tex$condition)[5] <- "Fact \\& Learner"
tikz(file = "../output/convergence_point.tex", width = 4.75, height = 2)
convergence_tex %>%
  filter(!is.na(convergence_point)) %>%
  ggplot(aes(x = condition, y = convergence_point)) +
  # facet_grid(dataset ~ ., labeller = label_wrap_gen()) +
  geom_jitter(aes(colour = condition), width = 0.1, height = 0, alpha = 0.1) +
  geom_violin(fill = NA) +
  expand_limits(y = 0) +
  scale_colour_brewer(type = "qual", palette = 7) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Convergence point") +
  theme_light()
dev.off()
null device 
          1 

As preregistered, we conduct a Bayesian ANOVA testing the effect of condition on the convergence point.

There is very strong evidence for this model compared to a null model.

convergence_dat <- convergence %>%
  ungroup() %>%
  filter(!is.na(convergence_point)) %>%
  mutate_if(is.character, as.factor) %>%
  mutate(fact_id = as.factor(fact_id))
bf_convergence <- lmBF(
  formula = convergence_point ~ condition * dataset + subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = convergence_dat,
  progress = FALSE
)
data coerced from tibble to data frame
  
bf_convergence  
Bayes factor analysis
--------------
[1] condition * dataset + subject + fact_id : 594003.3 ±1.41%

Against denominator:
  Intercept only 
---
Bayes factor type: BFlinearModel, JZS
1/bf_convergence
Bayes factor analysis
--------------
[1] Intercept only : 1.683492e-06 ±1.41%

Against denominator:
  convergence_point ~ condition * dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

To check whether we should test the two datasets separately, compare the first model to one without the interaction between condition and dataset. This comparison shows that there is strong evidence for the model without the interaction and against the model with interaction.

bf_convergence_nointeraction <- lmBF(
  formula = convergence_point ~ condition + dataset + subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = convergence_dat,
  progress = FALSE
)
data coerced from tibble to data frame
  
bf_convergence_nointeraction / bf_convergence
Bayes factor analysis
--------------
[1] condition + dataset + subject + fact_id : 23.70967 ±2.05%

Against denominator:
  convergence_point ~ condition * dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

A model that leaves out dataset altogether is supported even more strongly by the evidence, showing that we can assume no effect of dataset on the convergence point.

bf_convergence_nodataset <- lmBF(
  formula = convergence_point ~ condition + subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = convergence_dat,
  progress = FALSE
)
data coerced from tibble to data frame
bf_convergence_nodataset / bf_convergence_nointeraction
Bayes factor analysis
--------------
[1] condition + subject + fact_id : 10.27426 ±1.57%

Against denominator:
  convergence_point ~ condition + dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

Compare to the maximal model:

bf_convergence_nodataset / bf_convergence
Bayes factor analysis
--------------
[1] condition + subject + fact_id : 243.5993 ±1.5%

Against denominator:
  convergence_point ~ condition * dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

Compare the preferred simpler model to a model without condition effect:

bf_convergence_nocondition <-  lmBF(
  formula = convergence_point ~ subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = convergence_dat,
  progress = FALSE
)
data coerced from tibble to data frame
bf_convergence_nocondition / bf_convergence_nodataset 
Bayes factor analysis
--------------
[1] subject + fact_id : 11.60565 ±0.59%

Against denominator:
  convergence_point ~ condition + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS
bf_convergence_nocondition / bf_convergence_nointeraction
Bayes factor analysis
--------------
[1] subject + fact_id : 119.2395 ±1.51%

Against denominator:
  convergence_point ~ condition + dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS
bf_convergence_nocondition / bf_convergence
Bayes factor analysis
--------------
[1] subject + fact_id : 2827.129 ±1.43%

Against denominator:
  convergence_point ~ condition * dataset + subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

Although it's not the best model, we can sample from the posterior of the model with condition to see what it would predict:

samples <- posterior(bf_convergence_nodataset, iterations = 1000, progress = FALSE)
summary(samples[,1:6])

Iterations = 1:1000
Thinning interval = 1 
Number of chains = 1 
Sample size per chain = 1000 

1. Empirical mean and standard deviation for each variable,
   plus standard error of the mean:

                             Mean     SD Naive SE Time-series SE
mu                        8.19349 0.2730 0.008632       0.008070
condition-Default        -0.57755 0.2646 0.008367       0.008929
condition-Domain          0.04970 0.2697 0.008527       0.008527
condition-Fact            0.23992 0.2329 0.007366       0.007366
condition-Learner        -0.08596 0.2761 0.008732       0.008732
condition-Fact & Learner  0.37389 0.2815 0.008902       0.008902

2. Quantiles for each variable:

                            2.5%      25%      50%      75%    97.5%
mu                        7.6596  8.00411  8.20034  8.37465  8.71860
condition-Default        -1.0804 -0.75229 -0.59040 -0.39535 -0.03867
condition-Domain         -0.4836 -0.13903  0.04772  0.23367  0.57610
condition-Fact           -0.2158  0.08153  0.24698  0.40370  0.68763
condition-Learner        -0.6423 -0.26590 -0.07275  0.09449  0.43159
condition-Fact & Learner -0.1869  0.18210  0.38346  0.56493  0.92216

Exploratory: the effect of the RoF estimate on convergence

Convergence vs. no convergence

convergence_traces <- convergence %>%
  mutate(converges = !is.na(convergence_point)) %>%
  left_join(alpha_change, by = c("dataset", "subject", "condition", "fact_id"))

Trajectories

ggplot(convergence_traces, aes(x = repetition, y = alpha, colour = interaction(subject, fact_id))) +
  facet_grid(converges ~ condition) +
  geom_line(alpha = 0.1) +
  geom_point(alpha = 0.1) +
  guides(colour = FALSE) +
  labs(title = "Trajectories that do not converge (top) and those that do (bottom)",
       y = "Rate of forgetting")

ggplot(convergence_traces, aes(x = repetition, y = d_alpha, colour = interaction(subject, fact_id))) +
  facet_grid(converges ~ condition) +
  geom_line(alpha = 0.1) +
  geom_point(alpha = 0.1) +
  geom_hline(yintercept = c(-0.00496, 0.00496), lty = 2) +
  guides(colour = FALSE) +
  labs(title = "Trajectories that do not converge (top) and those that do (bottom)",
       y = "Change in rate of forgetting")

Indexed on the final value:

convergence_traces %>%
  group_by(dataset, subject, condition, fact_id) %>%
  mutate(alpha_endaligned = alpha - final_alpha) %>%
  ggplot(aes(x = repetition, y = alpha_endaligned, colour = interaction(subject, fact_id))) +
  facet_grid(converges ~ condition) +
  geom_line(alpha = 0.1) +
  geom_point(alpha = 0.1) +
  guides(colour = FALSE) +
  labs(title = "Trajectories that do not converge (top) and those that do (bottom)",
       y = "Rate of forgetting (indexed on final value)")

Convergence point ~ number of repetitions

The convergence point is by definition capped to the total number of repetitions a fact receives (at the latest, we can observe convergence at the final repetition). But it is also quite clear that convergence rarely happens before the final repetition, which requires more than two small changes in a row. Pearson's r: 0.9789794.

ggplot(filter(convergence, !is.na(convergence_point)), aes(x = total_reps, y = convergence_point)) +
  geom_jitter(alpha = 0.2) +
  coord_equal() +
  geom_abline(slope = 1, intercept = 0, lty = 2)

Do changes in RoF become smaller as the number of repetitions increases? As the plot below shows: yes, but up to a point. After about 7 repetitions the size of the change stays more or less constant.

convergence_traces %>%
  group_by(repetition) %>%
  summarise(mean_change = mean(abs_d_alpha),
            se_change = plotrix::std.error(abs_d_alpha)) %>%
  ggplot(aes(x = repetition, y = mean_change)) +
  geom_point() +
  geom_errorbar(aes(ymin= mean_change - se_change, ymax = mean_change + se_change)) +
  labs(x = "Repetition",
       y = "Mean change in RoF estimate",
       caption = "Error bars show +/- 1 standard error of the mean")

Convergence point ~ final RoF

The convergence point seems to depend on the final rate of forgetting: the higher the rate of forgetting, the later convergence happens. Importantly, this trend seems to exist in all conditions.

quantiles <- quantile(filter(distinct(convergence_traces, dataset, subject, condition, fact_id, converges, final_alpha), converges)$final_alpha,
                      probs = seq(0, 1, 0.2))
convergence_traces %>%
  filter(converges) %>%
  ungroup() %>%
  mutate(final_alpha_bin = cut(final_alpha, quantiles, include.lowest = TRUE)) %>%
  group_by(dataset, subject, condition, fact_id, final_alpha) %>%
  slice(n()) %>%
  ggplot(aes(x = final_alpha_bin, y = convergence_point)) +
  facet_grid(~ condition) +
  geom_jitter(aes(colour = final_alpha_bin), width = 0.2, alpha = 0.2) +
  geom_violin(fill = NA) +
  geom_boxplot(width = 0.2, outlier.shape = NA) +
  guides(colour = FALSE) +
  labs(x = "Final rate of forgetting (binned in quantiles)",
       y = "Convergence point") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5))

NA
convergence_final_rof <- distinct(convergence_traces, dataset, subject, condition, fact_id, final_alpha, converges, convergence_point) %>%
  ungroup() %>%
  filter(converges) %>%
  mutate(dataset = as.factor(dataset),
         subject = as.factor(subject),
         fact_id = as.factor(fact_id))
m_conv_final_rof <- lmer(convergence_point ~ final_alpha * condition  + (1 | subject) + (1 | fact_id), data = convergence_final_rof)
summary(m_conv_final_rof)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: convergence_point ~ final_alpha * condition + (1 | subject) +      (1 | fact_id)
   Data: convergence_final_rof

REML criterion at convergence: 3970.1

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.1512 -0.5529  0.0085  0.5986  6.1152 

Random effects:
 Groups   Name        Variance Std.Dev.
 subject  (Intercept) 0.05296  0.2301  
 fact_id  (Intercept) 0.01241  0.1114  
 Residual             3.68901  1.9207  
Number of obs: 958, groups:  subject, 284; fact_id, 30

Fixed effects:
                                    Estimate Std. Error       df t value Pr(>|t|)    
(Intercept)                          -8.2510     0.8499 910.8487  -9.708   <2e-16 ***
final_alpha                          38.0764     1.9958 935.2891  19.078   <2e-16 ***
conditionDomain                      -0.4480     1.2798 934.7219  -0.350    0.726    
conditionFact                        -0.7385     1.0884 908.8547  -0.678    0.498    
conditionLearner                     -0.3575     1.1759 889.9558  -0.304    0.761    
conditionFact & Learner              -1.4698     1.2512 881.5932  -1.175    0.240    
final_alpha:conditionDomain           1.3849     2.9387 945.7409   0.471    0.638    
final_alpha:conditionFact             1.9846     2.5038 931.8435   0.793    0.428    
final_alpha:conditionLearner          0.2059     2.6888 920.9964   0.077    0.939    
final_alpha:conditionFact & Learner   3.3828     2.8570 922.8715   1.184    0.237    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) fnl_lp cndtnD cndtnF cndtnL cndF&L fnl_:D fnl_:F fnl_:L
final_alpha -0.987                                                        
conditinDmn -0.663  0.655                                                 
conditinFct -0.779  0.769  0.518                                          
conditnLrnr -0.722  0.713  0.480  0.563                                   
cndtnFct&Lr -0.678  0.669  0.451  0.529  0.490                            
fnl_lph:cnD  0.670 -0.679 -0.987 -0.523 -0.484 -0.455                     
fnl_lph:cnF  0.785 -0.796 -0.522 -0.985 -0.568 -0.534  0.541              
fnl_lph:cnL  0.732 -0.741 -0.486 -0.571 -0.984 -0.497  0.504  0.591       
fnl_lph:F&L  0.688 -0.697 -0.458 -0.537 -0.498 -0.986  0.474  0.556  0.518

The lmer confirms a large, positive main effect of final rate of forgetting on convergence point, but no effect of, or interaction with, condition.

This implies that, regardless of the condition, convergence will happen sooner if the final estimate is lower.

Compare the final rate of forgetting distribution of estimates that did converge to those that did not.

convergence_traces %>%
  distinct(dataset, subject, condition, fact_id, .keep_all = TRUE) %>%
  ggplot(aes(x = converges, y = final_alpha)) +
  facet_grid(~ condition) +
  geom_jitter(aes(colour = converges), width = 0.2, alpha = 0.2) +
  geom_violin(fill = NA) +
  geom_boxplot(width = 0.2, outlier.shape = NA) +
  guides(colour = FALSE)

conv <- convergence_traces %>%
  distinct(dataset, subject, condition, fact_id, .keep_all = TRUE)
m_conv_final <- lmer(converges ~ final_alpha * condition + (1 | subject) + (1 | fact_id), data = conv)
summary(m_conv_final)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: converges ~ final_alpha * condition + (1 | subject) + (1 | fact_id)
   Data: conv

REML criterion at convergence: 4201

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.2839 -0.6142 -0.3359 -0.0556  2.4759 

Random effects:
 Groups   Name        Variance  Std.Dev.
 subject  (Intercept) 0.0009191 0.03032 
 fact_id  (Intercept) 0.0002195 0.01482 
 Residual             0.1511051 0.38872 
Number of obs: 4364, groups:  subject, 291; fact_id, 30

Fixed effects:
                                      Estimate Std. Error         df t value Pr(>|t|)    
(Intercept)                           -0.47314    0.06319 1455.58226  -7.488 1.21e-13 ***
final_alpha                            2.05334    0.16705 2217.00175  12.292  < 2e-16 ***
conditionDomain                        0.08191    0.09391 1893.61332   0.872   0.3832    
conditionFact                          0.07994    0.08209 2012.05410   0.974   0.3303    
conditionLearner                       0.10055    0.09120 1919.40134   1.102   0.2704    
conditionFact & Learner                0.08513    0.09204 2116.49983   0.925   0.3551    
final_alpha:conditionDomain           -0.52471    0.24141 2755.45593  -2.174   0.0298 *  
final_alpha:conditionFact             -0.52171    0.21251 2865.73697  -2.455   0.0141 *  
final_alpha:conditionLearner          -0.55949    0.23374 2716.39463  -2.394   0.0167 *  
final_alpha:conditionFact & Learner   -0.52013    0.23608 2983.60844  -2.203   0.0277 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) fnl_lp cndtnD cndtnF cndtnL cndF&L fnl_:D fnl_:F fnl_:L
final_alpha -0.971                                                        
conditinDmn -0.669  0.651                                                 
conditinFct -0.765  0.744  0.515                                          
conditnLrnr -0.689  0.670  0.464  0.530                                   
cndtnFct&Lr -0.682  0.664  0.460  0.526  0.473                            
fnl_lph:cnD  0.670 -0.689 -0.975 -0.516 -0.464 -0.460                     
fnl_lph:cnF  0.760 -0.782 -0.512 -0.973 -0.527 -0.522  0.542              
fnl_lph:cnL  0.691 -0.712 -0.465 -0.532 -0.973 -0.475  0.493  0.559       
fnl_lph:F&L  0.684 -0.704 -0.461 -0.527 -0.474 -0.974  0.488  0.554  0.504

Takeaway:

  • Estimates that do converge tend to have a higher final value than estimates that don't (which makes some sense, since there are more opportunities for adjustment/stabilising).
  • For estimates that converge, convergence happens sooner when the final value is lower.
  • Tradeoff: lower final value means less likely to converge, but when convergence does happen, it's faster.

Convergence point ~ initial RoF

Is the above also true for the initial rate of forgetting estimate? Maybe, judging by the plot below (note that the quantiles are recalculated based on initial alpha values).

quantiles <- quantile(filter(convergence_traces, converges, repetition == 1)$alpha,
                      probs = seq(0, 1, 0.2))
convergence_traces %>%
  filter(converges, repetition == 1) %>%
  ungroup() %>%
  mutate(alpha_bin = cut(alpha, quantiles, include.lowest = TRUE)) %>%
  ggplot(aes(x = alpha_bin, y = convergence_point)) +
  facet_grid(~ condition) +
  geom_jitter(aes(colour = alpha_bin), width = 0.2, alpha = 0.2) +
  geom_violin(fill = NA) +
  geom_boxplot(width = 0.2, outlier.shape = NA) +
  guides(colour = FALSE) +
  labs(x = "Initial rate of forgetting (binned in quantiles)",
       y = "Convergence point") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5))

convergence_init_rof <- convergence_traces %>%
  ungroup() %>%
  filter(converges, repetition == 1) %>%
  mutate(dataset = as.factor(dataset),
         subject = as.factor(subject),
         fact_id = as.factor(fact_id))
m_conv_init_rof <- lmer(convergence_point ~ alpha * condition  + (1 | subject) + (1 | fact_id), data = convergence_init_rof)
fixed-effect model matrix is rank deficient so dropping 1 column / coefficient
summary(m_conv_init_rof)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: convergence_point ~ alpha * condition + (1 | subject) + (1 |      fact_id)
   Data: convergence_init_rof

REML criterion at convergence: 5011

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.8958 -0.5854 -0.2257  0.3183  8.3311 

Random effects:
 Groups   Name        Variance Std.Dev.
 subject  (Intercept)  1.0468  1.0231  
 fact_id  (Intercept)  0.4639  0.6811  
 Residual             10.1190  3.1810  
Number of obs: 958, groups:  subject, 284; fact_id, 30

Fixed effects:
                        Estimate Std. Error       df t value Pr(>|t|)    
(Intercept)             -10.3766     3.3206 704.3402  -3.125  0.00185 ** 
alpha                    60.1350    11.0435 709.2184   5.445 7.14e-08 ***
conditionDomain          14.2744     9.9482 209.6585   1.435  0.15282    
conditionFact            10.6020     3.9116 809.2985   2.710  0.00686 ** 
conditionLearner          3.5360     4.4931 405.1176   0.787  0.43175    
conditionFact & Learner  -2.2479     0.7197 387.1621  -3.123  0.00192 ** 
alpha:conditionDomain   -47.2044    28.9796 220.5039  -1.629  0.10477    
alpha:conditionFact     -37.2203    12.2891 803.9285  -3.029  0.00253 ** 
alpha:conditionLearner  -18.4022    13.8335 460.6365  -1.330  0.18409    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) alpha  cndtnD cndtnF cndtnL cndF&L alph:D alph:F
alpha       -0.996                                                 
conditinDmn -0.328  0.328                                          
conditinFct -0.800  0.797  0.266                                   
conditnLrnr -0.744  0.742  0.245  0.592                            
cndtnFct&Lr  0.798 -0.831 -0.265 -0.635 -0.594                     
alph:cndtnD  0.374 -0.376 -0.998 -0.302 -0.279  0.315              
alph:cndtnF  0.852 -0.854 -0.283 -0.991 -0.631  0.709  0.324       
alph:cndtnL  0.799 -0.802 -0.263 -0.637 -0.992  0.666  0.301  0.683
fit warnings:
fixed-effect model matrix is rank deficient so dropping 1 column / coefficient

The lmer confirms that the convergence point increases as a function of the initial rate of forgetting (alpha) in all conditions, even accounting for the interaction between the initial alpha and the condition.

Convergence seems to be about equally likely for low and high initial values.

convergence_traces %>%
  distinct(dataset, subject, condition, fact_id, .keep_all = TRUE) %>%
  ggplot(aes(x = converges, y = alpha)) +
  facet_grid(~ condition) +
  geom_jitter(aes(colour = converges), width = 0.2, alpha = 0.2) +
  geom_violin(fill = NA) +
  geom_boxplot(width = 0.2, outlier.shape = NA) +
  guides(colour = FALSE)

The lack of an effect is confirmed by an lmer:

m_conv_init <- lmer(converges ~ alpha * condition + (1 | subject) + (1 | fact_id), data = conv)
fixed-effect model matrix is rank deficient so dropping 1 column / coefficient
summary(m_conv_init)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: converges ~ alpha * condition + (1 | subject) + (1 | fact_id)
   Data: conv

REML criterion at convergence: 4644.9

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.1161 -0.5731 -0.4636 -0.2834  2.2070 

Random effects:
 Groups   Name        Variance Std.Dev.
 subject  (Intercept) 0.003377 0.05811 
 fact_id  (Intercept) 0.003009 0.05486 
 Residual             0.164596 0.40570 
Number of obs: 4364, groups:  subject, 291; fact_id, 30

Fixed effects:
                          Estimate Std. Error         df t value Pr(>|t|)  
(Intercept)                0.39307    0.19291 1541.79816   2.038   0.0418 *
alpha                     -0.37481    0.63989 1589.07872  -0.586   0.5581  
conditionDomain            0.27153    0.56167  299.59644   0.483   0.6291  
conditionFact             -0.28802    0.22118 2636.45689  -1.302   0.1930  
conditionLearner          -0.39459    0.25428  683.42709  -1.552   0.1212  
conditionFact & Learner   -0.05310    0.04077  661.14912  -1.303   0.1932  
alpha:conditionDomain     -0.93541    1.63596  315.01413  -0.572   0.5679  
alpha:conditionFact        0.63970    0.69997 2480.83176   0.914   0.3609  
alpha:conditionLearner     0.97921    0.78805  820.90581   1.243   0.2144  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) alpha  cndtnD cndtnF cndtnL cndF&L alph:D alph:F
alpha       -0.995                                                 
conditinDmn -0.341  0.340                                          
conditinFct -0.800  0.797  0.274                                   
conditnLrnr -0.757  0.756  0.259  0.607                            
cndtnFct&Lr  0.769 -0.810 -0.263 -0.615 -0.584                     
alph:cndtnD  0.388 -0.390 -0.998 -0.311 -0.295  0.316              
alph:cndtnF  0.847 -0.851 -0.290 -0.991 -0.643  0.691  0.332       
alph:cndtnL  0.808 -0.813 -0.277 -0.647 -0.992  0.658  0.317  0.691
fit warnings:
fixed-effect model matrix is rank deficient so dropping 1 column / coefficient

We can surmise that the convergence point is sensitive to both the initial estimate and the final estimate. When conditions have unequal initial estimates, as is the case here (the initial estimates in the adaptive conditions tend to be higher than those in the default condition), this confounds the results.

It is clear that the operationalisation of the convergence point is problematic: one would come to the conclusion that a lower initial estimate is always better, simply because that is rewarded in the current analysis. We may want to think about a measure that is robust to this.

Ideas:

  • Split the data on initial estimate (lower than 0.3 or higher than 0.3) and see if convergence improves below 0.3.
  • ...

The lower convergence success in adaptive distributions may be because the final alpha might be higher in these conditions. There seems to be a slightly lower final rate of forgetting in the default condition compared to the adaptive conditions, but this mostly disappears in pairwise comparisons (also when we only look at converged estimates), and is also not supported by a Bayesian lmBF, which slightly prefers a model without condition.

p_final_rof <- ggplot(conv, aes(x = condition, y = final_alpha)) +
  geom_violin() +
  geom_jitter(alpha = 0.1, aes(colour = condition)) +
  geom_boxplot(outlier.shape = NA, width = 0.5) +
  labs(x = "Condition",
       y = "Final rate of forgetting") +
  guides(colour = FALSE)
tikz(file = "../output/final_rof_by_cond.tex", width = 4, height = 2.5)
p_final_rof +
  scale_x_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  theme_light()
dev.off()
null device 
          1 
p_final_rof

summary(m_final_rof_cond <- lmer(final_alpha ~ condition + (1 | subject) + (1 | fact_id), data = conv))
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: final_alpha ~ condition + (1 | subject) + (1 | fact_id)
   Data: conv

REML criterion at convergence: -10247.1

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.6263 -0.7031 -0.0942  0.5698  5.9523 

Random effects:
 Groups   Name        Variance Std.Dev.
 subject  (Intercept) 0.001176 0.03430 
 fact_id  (Intercept) 0.001135 0.03369 
 Residual             0.004902 0.07001 
Number of obs: 4364, groups:  subject, 291; fact_id, 30

Fixed effects:
                         Estimate Std. Error        df t value Pr(>|t|)    
(Intercept)             3.734e-01  8.221e-03 8.159e+01  45.417  < 2e-16 ***
conditionDomain         2.164e-02  7.691e-03 2.677e+02   2.813  0.00527 ** 
conditionFact           1.604e-02  6.932e-03 2.675e+02   2.314  0.02141 *  
conditionLearner        2.352e-02  7.654e-03 2.681e+02   3.072  0.00234 ** 
conditionFact & Learner 2.009e-02  7.724e-03 2.668e+02   2.601  0.00981 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) cndtnD cndtnF cndtnL
conditinDmn -0.470                     
conditinFct -0.522  0.558              
conditnLrnr -0.473  0.505  0.561       
cndtnFct&Lr -0.468  0.500  0.556  0.503
bf_final_rof_cond <- lmBF(
  formula = final_alpha ~ condition + subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = mutate(ungroup(conv), subject = as.factor(subject), fact_id = as.factor(fact_id)),
  progress = FALSE
)
data coerced from tibble to data frame
  
bf_final_rof_nocond <- lmBF(
  formula = final_alpha ~ subject + fact_id,
  whichRandom = c("subject", "fact_id"),
  data = mutate(ungroup(conv), subject = as.factor(subject), fact_id = as.factor(fact_id)),
  progress = FALSE
)
data coerced from tibble to data frame
  
bf_final_rof_cond / bf_final_rof_nocond 
Bayes factor analysis
--------------
[1] condition + subject + fact_id : 0.2735443 ±0.69%

Against denominator:
  final_alpha ~ subject + fact_id 
---
Bayes factor type: BFlinearModel, JZS

Tangential: how similar are final rates of forgetting for specific facts between conditions? Looks fairly consistent across conditions:

conv %>%
  group_by(condition, fact_id) %>%
  summarise(alpha = mean(final_alpha),
            n = n()) %>%
  # mutate(rank = dense_rank(desc(alpha))) %>%
  ggplot(aes(x = condition, y = alpha, group = fact_id, colour = as.factor(fact_id))) +
  geom_line(alpha = 0.75) +
  geom_point(aes(size = n)) +
  guides(colour = FALSE)


Make diagnostic plots:

library(patchwork)
# Change in rate of forgetting with each repetition
p_rof_change <- convergence_traces %>%
  group_by(repetition) %>%
  summarise(mean_change = mean(abs_d_alpha),
            se_change = plotrix::std.error(abs_d_alpha)) %>%
  ggplot(aes(x = repetition, y = mean_change)) +
  geom_point() +
  geom_errorbar(aes(ymin= mean_change - se_change, ymax = mean_change + se_change), na.rm = T) +
  geom_hline(yintercept = 0.00496, colour = "red", lty = 2) +
  labs(x = "Repetition",
       y = "Absolute rate of forgetting change")
  
# Convergence point ~ total number of repetitions
p_conv_reps <- ggplot(filter(convergence, !is.na(convergence_point)), aes(x = total_reps, y = convergence_point)) +
  geom_jitter(alpha = 0.2) +
  coord_equal() +
  geom_abline(slope = 1, intercept = 0, lty = 2) +
  labs(x = "Total repetitions",
      y = "Convergence point")
p_conv_point_initial <- ggplot(conv, aes(x = alpha, y = convergence_point, colour = condition, fill = condition)) +
  facet_grid(~ dataset) +
  geom_point(alpha = 0.1, na.rm = T) +
  geom_smooth(method = "lm", se = T, na.rm = T, size = .75) +
  # geom_rug(aes(y = NULL), sides = "b") +
  labs(x = "Initial rate of forgetting",
         y = "Convergence point")
p_conv_point_final <- ggplot(conv, aes(x = final_alpha, y = convergence_point, colour = condition, fill = condition)) +
  facet_grid(~ dataset) +
  geom_point(alpha = 0.1, na.rm = T) +
  geom_smooth(method = "lm", se = T, na.rm = T, size = .75) +
  # geom_rug(aes(y = NULL), sides = "b") +
  labs(x = "Final rate of forgetting",
         y = "Convergence point")
p_conv_prob_initial <- ggplot(conv, aes(x = alpha, y = as.numeric(converges), colour = condition, fill = condition)) +
  facet_grid(~ dataset) +
  geom_point(alpha = 0.1, na.rm = T) +
  geom_smooth(method = "glm",  method.args=list(family="binomial"), se = T, na.rm = T) +
  # geom_rug(aes(y = NULL), sides = "b") +
  ylim(0, 1) +
  labs(x = "Initial rate of forgetting",
       y = "Convergence probability")
p_conv_prob_final <- ggplot(conv, aes(x = final_alpha, y = as.numeric(converges), colour = condition, fill = condition)) +
  facet_grid(~ dataset) +
  geom_point(alpha = 0.1, na.rm = T) +
  geom_smooth(method = "glm",  method.args=list(family="binomial"), se = T, na.rm = T) +
  # geom_rug(aes(y = NULL), sides = "b") +
  ylim(0, 1) +
  labs(x = "Final rate of forgetting",
       y = "Convergence probability")
p_rof_change + p_conv_reps + p_conv_point_initial + p_conv_prob_initial + p_conv_point_final + p_conv_prob_final + plot_layout(ncol = 2, byrow = TRUE)
ggsave("../output/convergence_diagnostics.pdf", width = 12, height = 12)

Plot the RoF change plot for each condition separately:

p_rof_change_cond <- convergence_traces %>%
  group_by(condition, repetition) %>%
  summarise(mean_change = mean(abs_d_alpha),
            se_change = plotrix::std.error(abs_d_alpha)) %>%
  ggplot(aes(x = repetition, y = mean_change, colour = condition)) +
  geom_line() +
  geom_point() +
  geom_errorbar(aes(ymin= mean_change - se_change, ymax = mean_change + se_change), na.rm = T) +
  geom_hline(yintercept = 0.00496, colour = "red", lty = 2) +
  labs(x = "Repetition",
       y = "Absolute rate of forgetting change") +
  xlim(0,10) 
p_rof_change_cond

Also plot each diagnostic plot separately:

tikz(file = "../output/rof_change.tex", width = 4, height = 2.5)
p_rof_change +
  theme_light()
dev.off()
null device 
          1 
tikz(file = "../output/conv_reps.tex", width = 2.5, height = 2.5)
p_conv_reps +
  theme_light()
dev.off()
null device 
          1 
tikz(file = "../output/conv_point_initial_rof.tex", width = 4, height = 2)
p_conv_point_initial +
  scale_color_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  scale_fill_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  theme_light()
dev.off()
null device 
          1 
tikz(file = "../output/conv_point_final_rof.tex", width = 4, height = 2)
p_conv_point_final +
  scale_color_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  scale_fill_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  theme_light()
dev.off()
null device 
          1 
tikz(file = "../output/conv_prob_initial_rof.tex", width = 4, height = 2)
p_conv_prob_initial +
  scale_color_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  scale_fill_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  theme_light()
dev.off()
null device 
          1 
tikz(file = "../output/conv_prob_final_rof.tex", width = 4, height = 2)
p_conv_prob_final +
  scale_color_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  scale_fill_discrete(labels = c("Default", "Domain", "Fact", "Learner", "Fact \\& Learner")) +
  theme_light()
dev.off()
null device 
          1 

Session information

sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_GB.UTF-8       LC_NUMERIC=C               LC_TIME=nl_NL.UTF-8        LC_COLLATE=en_GB.UTF-8     LC_MONETARY=nl_NL.UTF-8    LC_MESSAGES=en_GB.UTF-8   
 [7] LC_PAPER=nl_NL.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=nl_NL.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] patchwork_0.0.1        lmerTest_3.1-0         lme4_1.1-21            tikzDevice_0.12        forcats_0.4.0          fst_0.9.0              tidyr_0.8.3           
 [8] stringr_1.4.0          readr_1.3.1            purrr_0.3.2            ggplot2_3.1.1          dplyr_0.8.0.1          BayesFactor_0.9.12-4.2 Matrix_1.2-17         
[15] coda_0.19-2           

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1          mvtnorm_1.0-10      lattice_0.20-38     gtools_3.8.1        assertthat_0.2.1    digest_0.6.18       R6_2.4.0            plyr_1.8.4         
 [9] MatrixModels_0.4-1  evaluate_0.13       pillar_1.4.0        rlang_0.3.4         lazyeval_0.2.2      rstudioapi_0.10     minqa_1.2.4         nloptr_1.2.1       
[17] rmarkdown_1.12      labeling_0.3        splines_3.6.1       munsell_0.5.0       compiler_3.6.1      numDeriv_2016.8-1.1 xfun_0.6            pkgconfig_2.0.2    
[25] base64enc_0.1-3     htmltools_0.3.6     tidyselect_0.2.5    tibble_2.1.1        crayon_1.3.4        withr_2.1.2         MASS_7.3-51.1       grid_3.6.1         
[33] nlme_3.1-140        jsonlite_1.6        gtable_0.3.0        magrittr_1.5        scales_1.0.0        stringi_1.4.3       pbapply_1.4-0       reshape2_1.4.3     
[41] boot_1.3-22         RColorBrewer_1.1-2  tools_3.6.1         glue_1.3.1          hms_0.4.2           plotrix_3.7-6       parallel_3.6.1      yaml_2.2.0         
[49] colorspace_1.4-1    filehash_2.4-2      knitr_1.22         
LS0tCnRpdGxlOiAiVGVzdCBvZiBINDogVGhlIHJhdGUgb2YgZm9yZ2V0dGluZyBlc3RpbWF0ZSBjb252ZXJnZXMgb24gaXRzIGZpbmFsIHZhbHVlIG1vcmUgcXVpY2tseSB3aGVuIGl0cyBpbml0aWFsIHZhbHVlIHdhcyBwcmVkaWN0ZWQgdXNpbmcgbGVhcm5pbmcgaGlzdG9yeSIKYXV0aG9yOiAiTWFhcnRlbiB2YW4gZGVyIFZlbGRlIgpkYXRlOiAiTGFzdCB1cGRhdGVkOiBgciBTeXMudGltZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBzbWFydDogbm8KICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMgT3ZlcnZpZXcKClRoaXMgbm90ZWJvb2sgY29udGFpbnMgdGhlIGFuYWx5c2lzIG9mIEh5cG90aGVzaXMgNCwgYXMgcHJlcmVnaXN0ZXJlZCBhdCBodHRwczovL29zZi5pby92d2c2dS86Cgo+IFRoZSBhYnNvbHV0ZSBkaXN0YW5jZSBiZXR3ZWVuIGludGVybWVkaWF0ZSBlc3RpbWF0ZXMgYW5kIHRoZSBmaW5hbCBlc3RpbWF0ZSBvZiB0aGUgcmF0ZSBvZiBmb3JnZXR0aW5nIGZvciBhIHBhcnRpY3VsYXIgbGVhcm5lci9mYWN0IGNvbWJpbmF0aW9uIGR1cmluZyBhIGxlYXJuaW5nIHNlc3Npb24gd2lsbCBjb252ZXJnZSBvbiB6ZXJvIChpLmUuLCBmYWxsIHdpdGhpbiBhIHNwZWNpZmllZCBib3VuZGFyeSBhYm92ZSB6ZXJvKSB3aXRoaW4gZmV3ZXIgYWRqdXN0bWVudHMgd2hlbiB0aGUgaW5pdGlhbCBlc3RpbWF0ZSBvZiB0aGUgcmF0ZSBvZiBmb3JnZXR0aW5nIHdhcyBiYXNlZCBvbiB0aGUgbW9zdCBwcmVkaWN0aXZlIGNvbWJpbmF0aW9uKHMpIG9mIGxlYXJuZXIgYW5kL29yIGZhY3QgaGlzdG9yeSB0aGFuIHdoZW4gdGhlIGluaXRpYWwgZXN0aW1hdGUgb2YgdGhlIHJhdGUgb2YgZm9yZ2V0dGluZyB3YXMgc2V0IGF0IHRoZSBkZWZhdWx0IHZhbHVlIG9mIDAuMy4KCiMgU2V0dXAKCiMjIExvYWQgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoQmF5ZXNGYWN0b3IpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZm9yY2F0cykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGlrekRldmljZSkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KGZzdCkKCnRoZW1lX3NldCh0aGVtZV9saWdodChiYXNlX3NpemUgPSAxNCkgKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIikpKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xNikgCmBgYAoKIyMgTG9hZCBkYXRhCmBgYHtyfQpyZXNwb25zZXNfbGFiXzJfd2l0aF9tb2RlbF9wYXJhbXMgPC0gcmVhZC5mc3QoZmlsZS5wYXRoKCIuLiIsICJkYXRhIiwgInByb2Nlc3NlZCIsICJsYWIiLCAic2Vzc2lvbjIiLCAic2Vzc2lvbjJfcmwyX3dpdGhfYWxwaGFfbGFiLmZzdCIpKQpyZXNwb25zZXNfbXR1cmtfMl93aXRoX21vZGVsX3BhcmFtcyA8LSByZWFkLmZzdChmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJvY2Vzc2VkIiwgIm10dXJrIiwgInNlc3Npb24yIiwgInNlc3Npb24yX3JsMl93aXRoX2FscGhhX210dXJrLmZzdCIpKQoKZml4X2NvbmRpdGlvbl9sYWJlbHMgPC0gZnVuY3Rpb24oeCkgewogIHJvd3dpc2UoeCkgJT4lCiAgbXV0YXRlKGNvbmRpdGlvbiA9IHN0cl9yZXBsYWNlKGNvbmRpdGlvbiwgIi1hbmQtIiwgIiAmICIpICU+JQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJzdHVkZW50IiwgImxlYXJuZXIiKSAlPiUKICAgICAgICAgICBzdHJfdG9fdGl0bGUoKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShjb25kaXRpb24gPSBmY3RfcmVsZXZlbChjb25kaXRpb24sICJGYWN0ICYgTGVhcm5lciIsIGFmdGVyID0gSW5mKSkgIyBNb3ZlIEYmTCBsZXZlbCB0byB0aGUgZW5kCn0KCmJsb2NrMl93aXRoX21vZGVsX3BhcmFtcyA8LSBiaW5kX3Jvd3MobXV0YXRlKHJlc3BvbnNlc19sYWJfMl93aXRoX21vZGVsX3BhcmFtcywgZGF0YXNldCA9ICJMYWIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUocmVzcG9uc2VzX210dXJrXzJfd2l0aF9tb2RlbF9wYXJhbXMsIGRhdGFzZXQgPSAiTVR1cmsiKSkgJT4lCiAgZml4X2NvbmRpdGlvbl9sYWJlbHMoKQpgYGAKCiMjIFN1YnNldCBkYXRhClRoZSBwcmVyZWdpc3RyYXRpb24gc3BlY2lmaWVkIDIyIEFwcmlsIDIwMTkgYXMgdGhlIGN1dG9mZiBkYXRlIGZvciBkYXRhIGNvbGxlY3Rpb24uCldlIHJlYWNoZWQgdGhlIG1pbmltdW0gb2YgMjUgcGFydGljaXBhbnRzIHBlciBjb25kaXRpb24gaW4gdGhlIGxhYiBzYW1wbGUgYmVmb3JlIHRoaXMgZGF0ZSwgYnV0IG5vdCBpbiB0aGUgTWVjaGFuaWNhbCBUdXJrIHNhbXBsZS4KRm9yIHRoYXQgcmVhc29uIGRhdGEgY29sbGVjdGlvbiBvbiBNVHVyayBjb250aW51ZWQgdW50aWwgdGhlcmUgd2VyZSBhdCBsZWFzdCAyNSBwYXJ0aWNpcGFudHMgcGVyIGNvbmRpdGlvbiAoMTQgTWF5IDIwMTkpIGFuZCB3YXMgc3RvcHBlZCBvbmx5IHRoZW4uClRoZSBhbmFseXNpcyByZXBvcnRlZCBpbiB0aGUgcGFwZXIgaXMgYmFzZWQgb25seSBvbiB0aGUgZGF0YSBmcm9tIGJlZm9yZSB0aGUgY3V0b2ZmIGRhdGUsIGJ1dCB0aGlzIG5vdGVib29rIGFsc28gcmVwb3J0cyB0aGUgc2FtZSBhbmFseXNpcyBkb25lIG9uIHRoZSBmdWxsIGRhdGFzZXQuIAoKYGBge3J9CnN1YmplY3RzX2N1dG9mZiA8LSByZWFkX2NzdihmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJvY2Vzc2VkIiwgInN1YmplY3RzX2JlZm9yZV9jdXRvZmYuY3N2IikpCgpibG9ja18yX2Z1bGwgPC0gYmxvY2syX3dpdGhfbW9kZWxfcGFyYW1zIAoKYmxvY2tfMl9jdXRvZmYgPC0gYmxvY2syX3dpdGhfbW9kZWxfcGFyYW1zICU+JQogIHJpZ2h0X2pvaW4oc3ViamVjdHNfY3V0b2ZmLCBieSA9ICJzdWJqZWN0IikKYGBgCgoKIyBDb252ZXJnZW5jZSBvZiB0aGUgZXN0aW1hdGUKCkVhY2ggdHJpYWwgaGFzIGEgdmFsdWUgYGFscGhhYCB0aGF0IGV4cHJlc3NlcyB0aGUgZXN0aW1hdGVkIHJhdGUgb2YgZm9yZ2V0dGluZyBvZiB0aGUgZmFjdCBhdCB0aGUgc3RhcnQgb2YgdGhlIHRyaWFsLgpUaGUgcmF0ZSBvZiBmb3JnZXR0aW5nIGVzdGltYXRlIHN0YXJ0cyBhdCB0aGUgcHJlZGljdGVkIHZhbHVlICh3aGljaCBkZXBlbmRzIG9uIHRoZSBjb25kaXRpb24pLgpJdCBpcyB0aGVuIHVwZGF0ZWQgYWZ0ZXIgZWFjaCByZXBldGl0aW9uIG9mIHRoZSBmYWN0IHRvIGJldHRlciBtYXRjaCB0aGUgb2JzZXJ2ZWQgcmVzcG9uc2VzLgoKYGBge3J9CmFscGhhX2NoYW5nZSA8LSBibG9ja18yX2Z1bGwgJT4lCiAgZ3JvdXBfYnkoZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkKSAlPiUKICBhcnJhbmdlKHN1YmplY3QsIGNvbmRpdGlvbiwgZmFjdF9pZCwgcmVwZXRpdGlvbikgJT4lCiAgbXV0YXRlKGRfYWxwaGEgPSBhbHBoYSAtIGxhZyhhbHBoYSkpICU+JQogIG11dGF0ZShkX2FscGhhID0gaWZlbHNlKGlzLm5hKGRfYWxwaGEpLCAwLCBkX2FscGhhKSkgJT4lCiAgbXV0YXRlKGFic19kX2FscGhhID0gYWJzKGRfYWxwaGEpKSAlPiUKICBtdXRhdGUoZmluYWxfYWxwaGEgPSB0YWlsKGFscGhhLDEpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGRhdGFzZXQsIHN1YmplY3QsIGNvbmRpdGlvbiwgZmFjdF9pZCwgcmVwZXRpdGlvbiwgYWxwaGEsIGRfYWxwaGEsIGFic19kX2FscGhhLCBmaW5hbF9hbHBoYSkKYGBgCgpUaGUgcGxvdCBiZWxvdyB2aXN1YWxpc2VzIHRoZSBkZXZlbG9wbWVudCBvZiBlYWNoIGFscGhhIGVzdGltYXRlIChldmVyeSBsZWFybmVyL2ZhY3QgY29tYmluYXRpb24gaXMgcmVwcmVzZW50ZWQgYnkgYSBsaW5lKS4KSXQgc2hvd3Mgc2V2ZXJhbCB0aGluZ3M6CgotIFRoZSBpbml0aWFsIHJhdGUgb2YgZm9yZ2V0dGluZyBlc3RpbWF0ZSBkaWZmZXJzIGRlcGVuZGluZyBvbiB0aGUgY29uZGl0aW9uOiBpbiBjb25kaXRpb25zIHdpdGggYSBkb21haW4td2lkZSBwcmVkaWN0aW9uIChEZWZhdWx0IGFuZCBEb21haW4pIHRoZSBzYW1lIHZhbHVlIGlzIGFsd2F5cyB1c2VkLCB3aGlsZSBpbiBjb25kaXRpb25zIHdpdGggbW9yZSBpbmRpdmlkdWFsaXNlZCBwcmVkaWN0aW9ucyAoRmFjdCBhbmQvb3IgTGVhcm5lcikgdGhlIHN0YXJ0aW5nIHZhbHVlIGRpZmZlcnMgYmV0d2VlbiBmYWN0cyBhbmQvb3IgbGVhcm5lcnMuCi0gVGhlIHJhdGUgb2YgZm9yZ2V0dGluZyBlc3RpbWF0ZSBpcyBvbmx5IGNoYW5nZWQgKmFmdGVyKiB0aGUgdGhpcmQgcHJlc2VudGF0aW9uIG9mIHRoZSBmYWN0LiBVcCB0byB0aGF0IHBvaW50IGl0IGlzIGFsd2F5cyBhIGhvcml6b250YWwgbGluZS4KLSBCZWNhdXNlIG9mIHRoZSB3YXkgdGhlIHNjaGVkdWxpbmcgYWxnb3JpdGhtIHdvcmtzLCB0aGVyZSBpcyBpbnZhcmlhYmx5IGEgcGx1bWUgdG93YXJkcyB0aGUgdG9wIHJpZ2h0OiBmYWN0cyB3aXRoIGEgbG93ZXIgcmF0ZSBvZiBmb3JnZXR0aW5nIGFyZSByZXBlYXRlZCBsZXNzIGZyZXF1ZW50bHkgdGhhbiBmYWN0cyB3aXRoIGEgaGlnaGVyIHJhdGUgb2YgZm9yZ2V0dGluZy4KCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KZ2dwbG90KGFscGhhX2NoYW5nZSwgYWVzKHggPSByZXBldGl0aW9uLCB5ID0gYWxwaGEsIGdyb3VwID0gaW50ZXJhY3Rpb24oc3ViamVjdCwgZmFjdF9pZCkpKSArCiAgZmFjZXRfZ3JpZChkYXRhc2V0IH4gY29uZGl0aW9uLCBsYWJlbGxlciA9IGxhYmVsX3dyYXBfZ2VuKCkpICsKICBnZW9tX2xpbmUoYWxwaGEgPSAwLjEpICsKICBsYWJzKHggPSAiUHJlc2VudGF0aW9uIiwKICAgICAgIHkgPSAiUmF0ZSBvZiBmb3JnZXR0aW5nIikKYGBgCgoKT3VyIHF1ZXN0aW9uIGlzIHdoZXRoZXIgdGhlIG1vZGVsIGNhbiBmaW5kIHRoZSBjb3JyZWN0IHJhdGUgb2YgZm9yZ2V0dGluZyBtb3JlIHF1aWNrbHkgaW4gY29uZGl0aW9ucyB0aGF0IHVzZSBhIHByZWRpY3RlZCB2YWx1ZSBhcyB0aGVpciBzdGFydGluZyBwb2ludC4KClRoZSBwbG90IGJlbG93IHNob3dzIHRoZSAqY2hhbmdlKiBpbiB0aGUgcmF0ZSBvZiBmb3JnZXR0aW5nIGVzdGltYXRlIGNvbXBhcmVkIHRvIHRoZSBwcmV2aW91cyBwcmVzZW50YXRpb24uCk5vdGUgdGhhdCBjaGFuZ2VzIGFyZSBjYXBwZWQgYXQgMC4wNDk2IGluIGJvdGggZGlyZWN0aW9ucy4KSXQgYXBwZWFycyB0aGF0IG1hbnkgb2YgdGhlIGNoYW5nZXMgYXJlIGFzIGxhcmdlIGFzIHBvc3NpYmxlLgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQpnZ3Bsb3QoYWxwaGFfY2hhbmdlLCBhZXMoeCA9IHJlcGV0aXRpb24sIHkgPSBkX2FscGhhLCBncm91cCA9IGludGVyYWN0aW9uKHN1YmplY3QsIGZhY3RfaWQpKSkgKwogIGZhY2V0X2dyaWQoZGF0YXNldCB+IGNvbmRpdGlvbiwgbGFiZWxsZXIgPSBsYWJlbF93cmFwX2dlbigpKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC4xKSArCiAgbGFicyh4ID0gIlByZXNlbnRhdGlvbiIsCiAgICAgICB5ID0gIkNoYW5nZSBpbiByYXRlIG9mIGZvcmdldHRpbmciKQpgYGAKCgpUaGUgZGlzdHJpYnV0aW9uIG9mIGNoYW5nZXMgKHNob3duIGJlbG93LCBleGNsdWRpbmcgdGhlIGZpcnN0IHRocmVlIHByZXNlbnRhdGlvbnMgc2luY2UgdGhleSBjYW4gbmV2ZXIgaGF2ZSBjaGFuZ2VzKSBjb25maXJtcyB0aGF0IG1hbnkgY2hhbmdlcyBhcmUgZWl0aGVyIGFzIGxhcmdlIGFzIHBvc3NpYmxlLCBvciBhbG1vc3QgemVyby4KVGhlIGRvdHRlZCBob3Jpem9udGFsIGxpbmVzIHNob3cgdGhlIGJvdW5kYXJpZXMgb2Ygd2hhdCB3ZSBjb25zaWRlciB0byBiZSB0aGUgY29udmVyZ2VuY2Ugem9uZSAoMC4wMDQ5NiBvbiBlaXRoZXIgc2lkZSBvZiB6ZXJvKS4KVGhpcyBwbG90IGFsc28gZ2l2ZXMgYW4gaW5kaWNhdGlvbiBvZiB0aGUgYmFsYW5jZSBiZXR3ZWVuIHVwd2FyZCBhbmQgZG93bndhcmQgY2hhbmdlcy4KSXQgaXMgZXNwZWNpYWxseSBhcHBhcmVudCB0aGF0IGNoYW5nZXMgaW4gdGhlIERlZmF1bHQgY29uZGl0aW9uIGFyZSBiaWFzZWQgdG93YXJkcyB1cHdhcmQgY2hhbmdlcywgd2hpY2ggbWFrZXMgc2Vuc2UgZ2l2ZW4gdGhlIGdlbmVyYWwgZGlmZnVsdHkgb2YgdGhlIG1hdGVyaWFsLgpJdCBsb29rcyBsaWtlIHRoZSBjaGFuZ2VzIG1heSBiZSBtb3JlIGJhbGFuY2VkIGluIG90aGVyIGNvbmRpdGlvbnMuCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CmFscGhhX2NoYW5nZSAlPiUKICBmaWx0ZXIocmVwZXRpdGlvbiA+IDMpICU+JQpnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBkX2FscGhhKSkgKwogIGZhY2V0X2dyaWQoZGF0YXNldCB+IC4sIGxhYmVsbGVyID0gbGFiZWxfd3JhcF9nZW4oKSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBjb25kaXRpb24pLCB3aWR0aCA9IDAuMSwgaGVpZ2h0ID0gMCwgYWxwaGEgPSAwLjEpICsKICBnZW9tX3Zpb2xpbihmaWxsID0gTkEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKDAuMDA0OTYsIC0wLjAwNDk2KSwgbHR5ID0gMikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDcpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDaGFuZ2UgaW4gcmF0ZSBvZiBmb3JnZXR0aW5nIiwKICAgICAgIGNhcHRpb24gPSAiRXhjbHVkaW5nIHRoZSBmaXJzdCAzIHRyaWFscywgaW4gd2hpY2ggUm9GIGNhbm5vdCBjaGFuZ2UuIikKYGBgCgoKYGBge3J9CmFscGhhX2NoYW5nZSAlPiUKICBmaWx0ZXIocmVwZXRpdGlvbiA+IDMpICU+JQpnZ3Bsb3QoYWVzKHggPSBkX2FscGhhKSkgKwogIGZhY2V0X2dyaWQoZGF0YXNldCB+IGNvbmRpdGlvbiwgbGFiZWxsZXIgPSBsYWJlbF93cmFwX2dlbigpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBhYnNfZF9hbHBoYSA8PSAwLjAwNDk2KSwgYmlud2lkdGggPSAwLjAwMikgKwogIGxhYnMoeCA9ICJDaGFuZ2UgaW4gcmF0ZSBvZiBmb3JnZXR0aW5nIiwKICAgICAgIGZpbGwgPSAiV2l0aGluXG5jb252ZXJnZW5jZVxuem9uZSIsCiAgICAgICBjYXB0aW9uID0gIkV4Y2x1ZGluZyB0aGUgZmlyc3QgMyB0cmlhbHMsIGluIHdoaWNoIFJvRiBjYW5ub3QgY2hhbmdlLiIpCgpgYGAKCgoKClRoZSBudW1iZXIgb2YgcHJlc2VudGF0aW9ucyBwZXIgZmFjdCwgd2hpY2ggd2UgZXhwZWN0ZWQgdG8gZHJvcCB3aXRoIGJldHRlciBwcmVkaWN0aW9uLCBpbiBmYWN0IHNlZW1zIHRvIGJlICpoaWdoZXIqIGluIHByZWRpY3RpdmUgY29uZGl0aW9ucywgaWYgYW55dGhpbmc6CgpgYGB7ciBmaWcud2lkdGg9MTJ9Cm5fcmVwcyA8LSBibG9ja18yX2Z1bGwgJT4lCiAgZ3JvdXBfYnkoZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkKSAlPiUKICBzdW1tYXJpc2UocmVwZXRpdGlvbnMgPSBtYXgocmVwZXRpdGlvbikpCgpnZ3Bsb3QoZmlsdGVyKG5fcmVwcywgcmVwZXRpdGlvbnMgPj0gNCksIGFlcyh4ID0gY29uZGl0aW9uLCB5ID0gcmVwZXRpdGlvbnMpKSArCiAgZmFjZXRfZ3JpZChkYXRhc2V0IH4gLikgKwogIGdlb21fdmlvbGluKCkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBjb25kaXRpb24pLCB3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAsIGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3VyX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gNykgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlJlcGV0aXRpb25zIiwKICAgICAgIGNhcHRpb24gPSAiRXhjbHVkaW5nIGZhY3RzIHdpdGggZmV3ZXIgdGhhbiA0IHJlcGV0aXRpb25zIikKYGBgCgpUaGlzIG1pZ2h0IHNpbXBseSBiZSBhbiBlZmZlY3Qgb2YgdGhlIGhpZ2ggZGlmZmljdWx0eSBvZiBpdGVtcyBvdmVyYWxsOiBpbiBtb3N0IGNhc2VzLCBpZiB0aGUgcmF0ZSBvZiBmb3JnZXR0aW5nIGlzIGVzdGltYXRlZCBtb3JlIGFjY3VyYXRlbHkgZnJvbSB0aGUgc3RhcnQsIHRoZSBpdGVtICpzaG91bGQqIGJlIHJlcGVhdGVkIG1vcmUgb2Z0ZW4gc2luY2UgaXQgaXMgY29ycmVjdGx5IGFzc2Vzc2VkIGFzIG1vcmUgZGlmZmljdWx0LgpXZSB3b3VsZCBleHBlY3QgdGhhdCBmb3IgdGhlIGVhc2llc3QgZmFjdHMgKG9mIHdoaWNoIHRoZXJlIGFyZSBvbmx5IGEgZmV3KSwgcHJlZGljdGlvbiBkb2VzIGRlY3JlYXNlIHJlcGV0aXRpb25zIGNvbXBhcmVkIHRvIHRoZSBkZWZhdWx0IGNvbmRpdGlvbi4KCkFzIGEgc2FuaXR5IGNoZWNrLCB2ZXJpZnkgdGhhdCB0aGUgbnVtYmVyIG9mIHJlcGV0aXRpb25zIHNjYWxlcyB3aXRoIHRoZSBmaW5hbCBhbHBoYSBlc3RpbWF0ZToKYGBge3IgZmlnLndpZHRoPTEyfQpuX3JlcHNfYnlfYWxwaGEgPC0gYmxvY2tfMl9mdWxsICU+JQogIGdyb3VwX2J5KGRhdGFzZXQsIHN1YmplY3QsIGNvbmRpdGlvbiwgZmFjdF9pZCkgJT4lCiAgc3VtbWFyaXNlKHJlcGV0aXRpb25zID0gbWF4KHJlcGV0aXRpb24pLAogICAgICAgICAgICBhbHBoYSA9IGFscGhhW3doaWNoLm1heChyZXBldGl0aW9uKV0pCgpnZ3Bsb3QoZmlsdGVyKG5fcmVwc19ieV9hbHBoYSwgcmVwZXRpdGlvbnMgPj0gNCksIGFlcyh4ID0gYWxwaGEsIHkgPSByZXBldGl0aW9ucywgY29sb3VyID0gY29uZGl0aW9uKSkgKwogIGZhY2V0X2dyaWQoZGF0YXNldCB+IGNvbmRpdGlvbiwgbGFiZWxsZXIgPSBsYWJlbF93cmFwX2dlbigpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSA3KSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKQpgYGAKCgpUaGUgcGxvdCBiZWxvdyBzaG93cyBvbmx5IHRoZSBudW1iZXIgb2YgcmVwZXRpdGlvbnMgZm9yIGNhc2VzIGluIHdoaWNoIHRoZSBmaW5hbCBhbHBoYSBpcyBsb3dlciB0aGFuIDAuMywgYW5kIGhpbnRzIGF0IGFuIGltcHJvdmVtZW50IChwYXJ0aWN1bGFybHkgaW4gdGhlIE1UdXJrIGRhdGEpLgpTdGFydGluZyBvZmYgd2l0aCBhIGxvd2VyLWZyZXF1ZW5jeSByZXBldGl0aW9uIHNjaGVkdWxlIGluZGVlZCBzZWVtcyB0byBsZWFkIHRvIGZld2VyIHJlcGV0aXRpb25zIG92ZXJhbGwuCgpgYGB7cn0Kbl9yZXBzX2J5X2FscGhhIDwtIGJsb2NrXzJfZnVsbCAlPiUKICBncm91cF9ieShkYXRhc2V0LCBzdWJqZWN0LCBjb25kaXRpb24sIGZhY3RfaWQpICU+JQogIHN1bW1hcmlzZShyZXBldGl0aW9ucyA9IG1heChyZXBldGl0aW9uKSwKICAgICAgICAgICAgYWxwaGEgPSBhbHBoYVt3aGljaC5tYXgocmVwZXRpdGlvbildKQoKZ2dwbG90KGZpbHRlcihuX3JlcHNfYnlfYWxwaGEsIHJlcGV0aXRpb25zID49IDQsIGFscGhhIDwgMC4zKSwgYWVzKHggPSBjb25kaXRpb24sIHkgPSByZXBldGl0aW9ucywgY29sb3VyID0gY29uZGl0aW9uKSkgKwogIGZhY2V0X2dyaWQoZGF0YXNldCB+IC4pICsKICBnZW9tX3Zpb2xpbigpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAsIGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3VyX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gNykgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlJlcGV0aXRpb25zIiwKICAgICAgIGNhcHRpb24gPSAiT25seSBzaG93aW5nIGZhY3RzIHdpdGggYWxwaGEgPCAwLjM7IGV4Y2x1ZGluZyBmYWN0cyB3aXRoIGZld2VyIHRoYW4gNCByZXBldGl0aW9ucyIpCmBgYAoKSW5kZWVkLCBhbiBleHBsb3JhdG9yeSBCYXllc2lhbiBBTk9WQSBzaG93cyBzdHJvbmcgZXZpZGVuY2UgZm9yIGFuIGVmZmVjdCBvZiBjb25kaXRpb24gb24gdGhlIG51bWJlciBvZiByZXBldGl0aW9ucyBhbW9uZyBsb3ctYWxwaGEgaXRlbXMgKGJ1dCBubyBlZmZlY3Qgb2YgZGF0YXNldCk6CmBgYHtyfQpuX3JlcHNfYnlfYWxwaGFfbG93IDwtIG5fcmVwc19ieV9hbHBoYSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlX2lmKGlzX2NoYXJhY3RlciwgYXMuZmFjdG9yKSAlPiUKICBmaWx0ZXIocmVwZXRpdGlvbnMgPj0gNCwgYWxwaGEgPCAwLjMpCgphbm92YUJGKAogIHJlcGV0aXRpb25zIH4gY29uZGl0aW9uICogZGF0YXNldCwKICBkYXRhID0gbl9yZXBzX2J5X2FscGhhX2xvdywKICBwcm9ncmVzcyA9IEZBTFNFCikKYGBgCgpGb2xsb3ctdXAgdC10ZXN0cyBpbmRpY2F0ZSB0aGF0IGFkYXBhdGlvbiBpbiBnZW5lcmFsIGhhcyBhIGJlbmVmaWNpYWwgZWZmZWN0Li4uCmBgYHtyfQp0dGVzdEJGKGZpbHRlcihuX3JlcHNfYnlfYWxwaGFfbG93LCBjb25kaXRpb24gPT0gIkRlZmF1bHQiKSRyZXBldGl0aW9ucywgZmlsdGVyKG5fcmVwc19ieV9hbHBoYV9sb3csIGNvbmRpdGlvbiAhPSAiRGVmYXVsdCIpJHJlcGV0aXRpb25zKQpgYGAKCi4uLiBidXQgdGhhdCB0aGVyZSBpcyBvbmx5IGFuZWNkb3RhbCBldmlkZW5jZSBzdXBwb3J0aW5nIGEgbW9yZSBpbmRpdmlkdWFsaXNlZCBhZGFwdGF0aW9uIG92ZXIgYSBkb21haW4td2lkZSBhZGFwdGF0aW9uOgpgYGB7cn0KdHRlc3RCRihmaWx0ZXIobl9yZXBzX2J5X2FscGhhX2xvdywgY29uZGl0aW9uID09ICJEb21haW4iKSRyZXBldGl0aW9ucywgZmlsdGVyKG5fcmVwc19ieV9hbHBoYV9sb3csIGNvbmRpdGlvbiAlaW4lIGMoIkZhY3QgJiBMZWFybmVyIiwgIkZhY3QiLCAiTGVhcm5lciIpKSRyZXBldGl0aW9ucykKYGBgCgoKCklmIHdlIHBsb3QgdGhlIGN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZiBjb252ZXJnZWQgZXN0aW1hdGVzLCB3ZSBzZWUgdGhhdCBlc3RpbWF0ZXMgaW4gdGhlIGRlZmF1bHQgY29uZGl0aW9uIGFyZSBtb3JlIGxpa2VseSB0byBjb252ZXJnZSB0aGFuIHRob3NlIGluIG90aGVyIGNvbmRpdGlvbnMtLS1ub3Qgd2hhdCB3ZSBleHBlY3RlZC4gIAoKCmBgYHtyIGZpZy53aWR0aD0xMn0KIyBjb252ZXJnZW5jZSA8LSBhbHBoYV9jaGFuZ2UgJT4lCiMgICBncm91cF9ieShkYXRhc2V0LCBzdWJqZWN0LCBjb25kaXRpb24sIGZhY3RfaWQpICU+JQojICAgZmlsdGVyKGFic19kX2FscGhhID4gMC4wMDQ5NikgJT4lICMgS2VlcCBjYXNlcyBpbiB3aGljaCB0aGUgYWRqdXN0bWVudCBpcyBvdXRzaWRlIHRoZSB3aW5kb3cKIyAgIHN1bW1hcmlzZShjb252ZXJnZW5jZV9wb2ludCA9IG1heChyZXBldGl0aW9uKSArIDEpICU+JQojICAgdW5ncm91cCgpICU+JQojICAgbXV0YXRlKGRhdGFzZXQgPSBhcy5mYWN0b3IoZGF0YXNldCksCiMgICAgICAgICAgc3ViamVjdCA9IGFzLmZhY3RvcihzdWJqZWN0KSwKIyAgICAgICAgICBjb25kaXRpb24gPSBhcy5mYWN0b3IoY29uZGl0aW9uKSwKIyAgICAgICAgICBmYWN0X2lkID0gYXMuZmFjdG9yKGZhY3RfaWQpKQoKCiMgY29udmVyZ2VuY2UgPC0gYWxwaGFfY2hhbmdlICU+JQojICAgZ3JvdXBfYnkoZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkKSAlPiUKIyAgIG11dGF0ZSh3aXRoaW5fYm91bmRhcnkgPSBhYnNfZF9hbHBoYSA8PSAwLjAwNDk2KSAlPiUKIyAgIG11dGF0ZSh0b3RhbF9yZXBzID0gbWF4KHJlcGV0aXRpb24pKSAlPiUKIyAgIGZpbHRlcighd2l0aGluX2JvdW5kYXJ5KSAlPiUKIyAgIHN1bW1hcmlzZSh0b3RhbF9yZXBzID0gdG90YWxfcmVwc1sxXSwgY29udmVyZ2VuY2VfcG9pbnQgPSBtYXgocmVwZXRpdGlvbikgKyAxLCBmaW5hbF9hbHBoYSA9IGFscGhhW3doaWNoLm1heChyZXBldGl0aW9uKV0pICU+JQojICAgbXV0YXRlKGNvbnZlcmdlbmNlX3BvaW50ID0gaWZlbHNlKGNvbnZlcmdlbmNlX3BvaW50ID4gdG90YWxfcmVwcywgTkEsIGNvbnZlcmdlbmNlX3BvaW50KSkKCmNvbnZlcmdlbmNlIDwtIGFscGhhX2NoYW5nZSAlPiUKICBncm91cF9ieShkYXRhc2V0LCBzdWJqZWN0LCBjb25kaXRpb24sIGZhY3RfaWQpICU+JQogIGZpbHRlcihtYXgocmVwZXRpdGlvbikgPiAzKSAlPiUKICBtdXRhdGUod2l0aGluX2JvdW5kYXJ5ID0gYWJzX2RfYWxwaGEgPD0gMC4wMDQ5NikgJT4lCiAgbXV0YXRlKHRvdGFsX3JlcHMgPSBtYXgocmVwZXRpdGlvbikpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9yZXBzID0gdG90YWxfcmVwc1sxXSwKICAgICAgICAgICAgY29udmVyZ2VuY2VfcG9pbnQgPSBsYXN0KHJlcGV0aXRpb25bIXdpdGhpbl9ib3VuZGFyeV0pICsgMSkgJT4lCiAgbXV0YXRlKGNvbnZlcmdlbmNlX3BvaW50ID0gaWZlbHNlKGNvbnZlcmdlbmNlX3BvaW50ID4gdG90YWxfcmVwcywgTkEsIGNvbnZlcmdlbmNlX3BvaW50KSkKCgpjb252ZXJnZW5jZV9jdW11bGF0aXZlIDwtIGNvbnZlcmdlbmNlICU+JQogIGdyb3VwX2J5KGRhdGFzZXQsIGNvbmRpdGlvbikgJT4lCiAgY291bnQoY29udmVyZ2VuY2VfcG9pbnQpICU+JQogIGNvbXBsZXRlKG5lc3RpbmcoZGF0YXNldCwgY29uZGl0aW9uKSwgY29udmVyZ2VuY2VfcG9pbnQgPSBjKDA6bWF4KGNvbnZlcmdlbmNlJGNvbnZlcmdlbmNlX3BvaW50LCBuYS5ybSA9IFQpLCBOQSksIGZpbGwgPSBsaXN0KG4gPSAwKSkgJT4lCiAgbXV0YXRlKHBlcmNfY29udmVyZ2VkID0gbiAvIHN1bShuKSkgJT4lCiAgbXV0YXRlKHBlcmNfY29udmVyZ2VkID0gY3Vtc3VtKHBlcmNfY29udmVyZ2VkKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGF0YXNldCwgY29uZGl0aW9uLCBjb252ZXJnZW5jZV9wb2ludCkgJT4lCiAgZmlsbChwZXJjX2NvbnZlcmdlZCkKICAKCmNvbnZlcmdlbmNlX2N1bXVsYXRpdmVfY29tYmluZWQgPC0gY29udmVyZ2VuY2UgJT4lCiAgZ3JvdXBfYnkoY29uZGl0aW9uKSAlPiUKICBjb3VudChjb252ZXJnZW5jZV9wb2ludCkgJT4lCiAgY29tcGxldGUobmVzdGluZyhjb25kaXRpb24pLCBjb252ZXJnZW5jZV9wb2ludCA9IGMoMDptYXgoY29udmVyZ2VuY2UkY29udmVyZ2VuY2VfcG9pbnQsIG5hLnJtID0gVCksIE5BKSwgZmlsbCA9IGxpc3QobiA9IDApKSAlPiUKICBtdXRhdGUocGVyY19jb252ZXJnZWQgPSBuIC8gc3VtKG4pKSAlPiUKICBtdXRhdGUocGVyY19jb252ZXJnZWQgPSBjdW1zdW0ocGVyY19jb252ZXJnZWQpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShjb25kaXRpb24sIGNvbnZlcmdlbmNlX3BvaW50KSAlPiUKICBmaWxsKHBlcmNfY29udmVyZ2VkKQoKYGBgCgoKCmBgYHtyIGZpZy53aWR0aD0xMn0KY29udmVyZ2VuY2VfY3VtdWxhdGl2ZV9jb21iaW5lZCAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvbnZlcmdlbmNlX3BvaW50KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gY29udmVyZ2VuY2VfcG9pbnQsIHkgPSBwZXJjX2NvbnZlcmdlZCwgY29sb3VyID0gY29uZGl0aW9uLCBsdHkgPSBjb25kaXRpb24pKSArCiAgIyBmYWNldF93cmFwKH4gZGF0YXNldCkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDcpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gMCwgeSA9IDApICsKICBsYWJzKHggPSAiUmVwZXRpdGlvbiIsCiAgICAgICB5ID0gIkVzdGltYXRlcyBjb252ZXJnZWQiLAogICAgICAgY29sb3VyID0gIkNvbmRpdGlvbiIsCiAgICAgICBsdHkgPSAiQ29uZGl0aW9uIikKICAgICAgICMgY2FwdGlvbiA9ICJDb252ZXJnZW5jZSBtZWFucyB0aGF0IHRoZXJlIGFyZSBubyBtb3JlIGFkanVzdG1lbnRzIGxhcmdlciB0aGFuIDAuMDA0OTYiKQoKZ2dzYXZlKCIuLi9vdXRwdXQvY3VtX3BlcmNfY29udmVyZ2VkLnBkZiIsIGRldmljZSA9ICJwZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmBgYAoKTWFrZSB0aGUgcGxvdCBzaG93biBpbiB0aGUgcGFwZXI6CmBgYHtyfQpjb252ZXJnZW5jZV9jdW11bGF0aXZlX2NvbWJpbmVkX3RleCA8LSBjb252ZXJnZW5jZV9jdW11bGF0aXZlX2NvbWJpbmVkCmxldmVscyhjb252ZXJnZW5jZV9jdW11bGF0aXZlX2NvbWJpbmVkX3RleCRjb25kaXRpb24pWzVdIDwtICJGYWN0IFxcJiBMZWFybmVyIgoKdGlreihmaWxlID0gIi4uL291dHB1dC9jdW1fcGVyY19jb252ZXJnZWQudGV4Iiwgd2lkdGggPSA0Ljc1LCBoZWlnaHQgPSAyKQoKY29udmVyZ2VuY2VfY3VtdWxhdGl2ZV9jb21iaW5lZF90ZXggJT4lCiAgZmlsdGVyKCFpcy5uYShjb252ZXJnZW5jZV9wb2ludCkpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbnZlcmdlbmNlX3BvaW50LCB5ID0gcGVyY19jb252ZXJnZWQsIGNvbG91ciA9IGNvbmRpdGlvbiwgbHR5ID0gY29uZGl0aW9uKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDcpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChzdWZmaXggPSAiXFwlIikpICsKICBleHBhbmRfbGltaXRzKHggPSAwLCB5ID0gMCkgKwogIGxhYnMoeCA9ICJSZXBldGl0aW9uIiwKICAgICAgIHkgPSAiRXN0aW1hdGVzIGNvbnZlcmdlZCIsCiAgICAgICBjb2xvdXIgPSAiQ29uZGl0aW9uIiwKICAgICAgIGx0eSA9ICJDb25kaXRpb24iKSArCiAgdGhlbWVfbGlnaHQoKQoKZGV2Lm9mZigpCmBgYAoKCgpXaGF0IHByb3BvcnRpb24gb2YgZXN0aW1hdGVzIGNvbnZlcmdlZCBpbiBlYWNoIGNvbmRpdGlvbj8KYGBge3J9CmNvbnZlcmdlbmNlX2N1bXVsYXRpdmUgJT4lCiAgZ3JvdXBfYnkoZGF0YXNldCwgY29uZGl0aW9uKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvbnZlcmdlbmNlX3BvaW50KSkgJT4lCiAgc3VtbWFyaXNlKG5fY29udmVyZ2VkID0gc3VtKG4pLAogICAgICAgICAgICBwZXJjX2NvbnZlcmdlZCA9IG1heChwZXJjX2NvbnZlcmdlZCkpCmBgYAoKCgpGb3IgdGhlIGVzdGltYXRlcyB0aGF0IGRpZCBjb252ZXJnZSwgcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNvbnZlcmdlbmNlIHBvaW50cyBieSBjb25kaXRpb246CmBgYHtyfQpjb252ZXJnZW5jZSAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvbnZlcmdlbmNlX3BvaW50KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gY29uZGl0aW9uLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQpKSArCiAgIyBmYWNldF9ncmlkKGRhdGFzZXQgfiAuLCBsYWJlbGxlciA9IGxhYmVsX3dyYXBfZ2VuKCkpICsKICBnZW9tX2ppdHRlcihhZXMoY29sb3VyID0gY29uZGl0aW9uKSwgd2lkdGggPSAwLjEsIGhlaWdodCA9IDAsIGFscGhhID0gMC4xKSArCiAgZ2VvbV92aW9saW4oZmlsbCA9IE5BKSArCiAgZXhwYW5kX2xpbWl0cyh5ID0gMCkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDcpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDb252ZXJnZW5jZSBwb2ludCIpCgpnZ3NhdmUoIi4uL291dHB1dC9jb252ZXJnZW5jZV9wb2ludC5wZGYiLCBkZXZpY2UgPSAicGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQoKYGBgCgpNYWtlIHRoZSBwbG90IHNob3duIGluIHRoZSBwYXBlcjoKYGBge3J9CmNvbnZlcmdlbmNlX3RleCA8LSBjb252ZXJnZW5jZQpsZXZlbHMoY29udmVyZ2VuY2VfdGV4JGNvbmRpdGlvbilbNV0gPC0gIkZhY3QgXFwmIExlYXJuZXIiCgp0aWt6KGZpbGUgPSAiLi4vb3V0cHV0L2NvbnZlcmdlbmNlX3BvaW50LnRleCIsIHdpZHRoID0gNC43NSwgaGVpZ2h0ID0gMikKCmNvbnZlcmdlbmNlX3RleCAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvbnZlcmdlbmNlX3BvaW50KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gY29uZGl0aW9uLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQpKSArCiAgIyBmYWNldF9ncmlkKGRhdGFzZXQgfiAuLCBsYWJlbGxlciA9IGxhYmVsX3dyYXBfZ2VuKCkpICsKICBnZW9tX2ppdHRlcihhZXMoY29sb3VyID0gY29uZGl0aW9uKSwgd2lkdGggPSAwLjEsIGhlaWdodCA9IDAsIGFscGhhID0gMC4xKSArCiAgZ2VvbV92aW9saW4oZmlsbCA9IE5BKSArCiAgZXhwYW5kX2xpbWl0cyh5ID0gMCkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDcpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDb252ZXJnZW5jZSBwb2ludCIpICsKICB0aGVtZV9saWdodCgpCgpkZXYub2ZmKCkKYGBgCgoKCkFzIHByZXJlZ2lzdGVyZWQsIHdlIGNvbmR1Y3QgYSBCYXllc2lhbiBBTk9WQSB0ZXN0aW5nIHRoZSBlZmZlY3Qgb2YgY29uZGl0aW9uIG9uIHRoZSBjb252ZXJnZW5jZSBwb2ludC4KClRoZXJlIGlzIHZlcnkgc3Ryb25nIGV2aWRlbmNlIGZvciB0aGlzIG1vZGVsIGNvbXBhcmVkIHRvIGEgbnVsbCBtb2RlbC4KYGBge3J9CmNvbnZlcmdlbmNlX2RhdCA8LSBjb252ZXJnZW5jZSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKCFpcy5uYShjb252ZXJnZW5jZV9wb2ludCkpICU+JQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgJT4lCiAgbXV0YXRlKGZhY3RfaWQgPSBhcy5mYWN0b3IoZmFjdF9pZCkpCgpiZl9jb252ZXJnZW5jZSA8LSBsbUJGKAogIGZvcm11bGEgPSBjb252ZXJnZW5jZV9wb2ludCB+IGNvbmRpdGlvbiAqIGRhdGFzZXQgKyBzdWJqZWN0ICsgZmFjdF9pZCwKICB3aGljaFJhbmRvbSA9IGMoInN1YmplY3QiLCAiZmFjdF9pZCIpLAogIGRhdGEgPSBjb252ZXJnZW5jZV9kYXQsCiAgcHJvZ3Jlc3MgPSBGQUxTRQopCiAgCmJmX2NvbnZlcmdlbmNlICAKCjEvYmZfY29udmVyZ2VuY2UKYGBgClRvIGNoZWNrIHdoZXRoZXIgd2Ugc2hvdWxkIHRlc3QgdGhlIHR3byBkYXRhc2V0cyBzZXBhcmF0ZWx5LCBjb21wYXJlIHRoZSBmaXJzdCBtb2RlbCB0byBvbmUgd2l0aG91dCB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBjb25kaXRpb24gYW5kIGRhdGFzZXQuClRoaXMgY29tcGFyaXNvbiBzaG93cyB0aGF0IHRoZXJlIGlzIHN0cm9uZyBldmlkZW5jZSBmb3IgdGhlIG1vZGVsIHdpdGhvdXQgdGhlIGludGVyYWN0aW9uIGFuZCBhZ2FpbnN0IHRoZSBtb2RlbCB3aXRoIGludGVyYWN0aW9uLgpgYGB7cn0KYmZfY29udmVyZ2VuY2Vfbm9pbnRlcmFjdGlvbiA8LSBsbUJGKAogIGZvcm11bGEgPSBjb252ZXJnZW5jZV9wb2ludCB+IGNvbmRpdGlvbiArIGRhdGFzZXQgKyBzdWJqZWN0ICsgZmFjdF9pZCwKICB3aGljaFJhbmRvbSA9IGMoInN1YmplY3QiLCAiZmFjdF9pZCIpLAogIGRhdGEgPSBjb252ZXJnZW5jZV9kYXQsCiAgcHJvZ3Jlc3MgPSBGQUxTRQopCiAgCmJmX2NvbnZlcmdlbmNlX25vaW50ZXJhY3Rpb24gLyBiZl9jb252ZXJnZW5jZQpgYGAKCkEgbW9kZWwgdGhhdCBsZWF2ZXMgb3V0IGRhdGFzZXQgYWx0b2dldGhlciBpcyBzdXBwb3J0ZWQgZXZlbiBtb3JlIHN0cm9uZ2x5IGJ5IHRoZSBldmlkZW5jZSwgc2hvd2luZyB0aGF0IHdlIGNhbiBhc3N1bWUgbm8gZWZmZWN0IG9mIGRhdGFzZXQgb24gdGhlIGNvbnZlcmdlbmNlIHBvaW50LgpgYGB7cn0KYmZfY29udmVyZ2VuY2Vfbm9kYXRhc2V0IDwtIGxtQkYoCiAgZm9ybXVsYSA9IGNvbnZlcmdlbmNlX3BvaW50IH4gY29uZGl0aW9uICsgc3ViamVjdCArIGZhY3RfaWQsCiAgd2hpY2hSYW5kb20gPSBjKCJzdWJqZWN0IiwgImZhY3RfaWQiKSwKICBkYXRhID0gY29udmVyZ2VuY2VfZGF0LAogIHByb2dyZXNzID0gRkFMU0UKKQoKYmZfY29udmVyZ2VuY2Vfbm9kYXRhc2V0IC8gYmZfY29udmVyZ2VuY2Vfbm9pbnRlcmFjdGlvbgpgYGAKCkNvbXBhcmUgdG8gdGhlIG1heGltYWwgbW9kZWw6CmBgYHtyfQpiZl9jb252ZXJnZW5jZV9ub2RhdGFzZXQgLyBiZl9jb252ZXJnZW5jZQpgYGAKCgpDb21wYXJlIHRoZSBwcmVmZXJyZWQgc2ltcGxlciBtb2RlbCB0byBhIG1vZGVsIHdpdGhvdXQgY29uZGl0aW9uIGVmZmVjdDoKYGBge3J9CmJmX2NvbnZlcmdlbmNlX25vY29uZGl0aW9uIDwtICBsbUJGKAogIGZvcm11bGEgPSBjb252ZXJnZW5jZV9wb2ludCB+IHN1YmplY3QgKyBmYWN0X2lkLAogIHdoaWNoUmFuZG9tID0gYygic3ViamVjdCIsICJmYWN0X2lkIiksCiAgZGF0YSA9IGNvbnZlcmdlbmNlX2RhdCwKICBwcm9ncmVzcyA9IEZBTFNFCikKCmJmX2NvbnZlcmdlbmNlX25vY29uZGl0aW9uIC8gYmZfY29udmVyZ2VuY2Vfbm9kYXRhc2V0IApgYGAKCmBgYHtyfQpiZl9jb252ZXJnZW5jZV9ub2NvbmRpdGlvbiAvIGJmX2NvbnZlcmdlbmNlX25vaW50ZXJhY3Rpb24KYmZfY29udmVyZ2VuY2Vfbm9jb25kaXRpb24gLyBiZl9jb252ZXJnZW5jZQpgYGAKCgpBbHRob3VnaCBpdCdzIG5vdCB0aGUgYmVzdCBtb2RlbCwgd2UgY2FuIHNhbXBsZSBmcm9tIHRoZSBwb3N0ZXJpb3Igb2YgdGhlIG1vZGVsICp3aXRoKiBjb25kaXRpb24gdG8gc2VlIHdoYXQgaXQgd291bGQgcHJlZGljdDoKYGBge3J9CnNhbXBsZXMgPC0gcG9zdGVyaW9yKGJmX2NvbnZlcmdlbmNlX25vZGF0YXNldCwgaXRlcmF0aW9ucyA9IDEwMDAsIHByb2dyZXNzID0gRkFMU0UpCnN1bW1hcnkoc2FtcGxlc1ssMTo2XSkKYGBgCgoKCgotLS0KCiMgRXhwbG9yYXRvcnk6IHRoZSBlZmZlY3Qgb2YgdGhlIFJvRiBlc3RpbWF0ZSBvbiBjb252ZXJnZW5jZQoKIyMgQ29udmVyZ2VuY2UgdnMuIG5vIGNvbnZlcmdlbmNlCgpgYGB7cn0KY29udmVyZ2VuY2VfdHJhY2VzIDwtIGNvbnZlcmdlbmNlICU+JQogIG11dGF0ZShjb252ZXJnZXMgPSAhaXMubmEoY29udmVyZ2VuY2VfcG9pbnQpKSAlPiUKICBsZWZ0X2pvaW4oYWxwaGFfY2hhbmdlLCBieSA9IGMoImRhdGFzZXQiLCAic3ViamVjdCIsICJjb25kaXRpb24iLCAiZmFjdF9pZCIpKQpgYGAKCiMjIyBUcmFqZWN0b3JpZXMKYGBge3J9CmdncGxvdChjb252ZXJnZW5jZV90cmFjZXMsIGFlcyh4ID0gcmVwZXRpdGlvbiwgeSA9IGFscGhhLCBjb2xvdXIgPSBpbnRlcmFjdGlvbihzdWJqZWN0LCBmYWN0X2lkKSkpICsKICBmYWNldF9ncmlkKGNvbnZlcmdlcyB+IGNvbmRpdGlvbikgKwogIGdlb21fbGluZShhbHBoYSA9IDAuMSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlRyYWplY3RvcmllcyB0aGF0IGRvIG5vdCBjb252ZXJnZSAodG9wKSBhbmQgdGhvc2UgdGhhdCBkbyAoYm90dG9tKSIsCiAgICAgICB5ID0gIlJhdGUgb2YgZm9yZ2V0dGluZyIpCgpnZ3Bsb3QoY29udmVyZ2VuY2VfdHJhY2VzLCBhZXMoeCA9IHJlcGV0aXRpb24sIHkgPSBkX2FscGhhLCBjb2xvdXIgPSBpbnRlcmFjdGlvbihzdWJqZWN0LCBmYWN0X2lkKSkpICsKICBmYWNldF9ncmlkKGNvbnZlcmdlcyB+IGNvbmRpdGlvbikgKwogIGdlb21fbGluZShhbHBoYSA9IDAuMSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKC0wLjAwNDk2LCAwLjAwNDk2KSwgbHR5ID0gMikgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiVHJhamVjdG9yaWVzIHRoYXQgZG8gbm90IGNvbnZlcmdlICh0b3ApIGFuZCB0aG9zZSB0aGF0IGRvIChib3R0b20pIiwKICAgICAgIHkgPSAiQ2hhbmdlIGluIHJhdGUgb2YgZm9yZ2V0dGluZyIpCgpgYGAKCgpJbmRleGVkIG9uIHRoZSBmaW5hbCB2YWx1ZToKYGBge3J9CmNvbnZlcmdlbmNlX3RyYWNlcyAlPiUKICBncm91cF9ieShkYXRhc2V0LCBzdWJqZWN0LCBjb25kaXRpb24sIGZhY3RfaWQpICU+JQogIG11dGF0ZShhbHBoYV9lbmRhbGlnbmVkID0gYWxwaGEgLSBmaW5hbF9hbHBoYSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVwZXRpdGlvbiwgeSA9IGFscGhhX2VuZGFsaWduZWQsIGNvbG91ciA9IGludGVyYWN0aW9uKHN1YmplY3QsIGZhY3RfaWQpKSkgKwogIGZhY2V0X2dyaWQoY29udmVyZ2VzIH4gY29uZGl0aW9uKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC4xKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiVHJhamVjdG9yaWVzIHRoYXQgZG8gbm90IGNvbnZlcmdlICh0b3ApIGFuZCB0aG9zZSB0aGF0IGRvIChib3R0b20pIiwKICAgICAgIHkgPSAiUmF0ZSBvZiBmb3JnZXR0aW5nIChpbmRleGVkIG9uIGZpbmFsIHZhbHVlKSIpCgpgYGAKCgojIyMgQ29udmVyZ2VuY2UgcG9pbnQgfiBudW1iZXIgb2YgcmVwZXRpdGlvbnMKClRoZSBjb252ZXJnZW5jZSBwb2ludCBpcyBieSBkZWZpbml0aW9uIGNhcHBlZCB0byB0aGUgdG90YWwgbnVtYmVyIG9mIHJlcGV0aXRpb25zIGEgZmFjdCByZWNlaXZlcyAoYXQgdGhlIGxhdGVzdCwgd2UgY2FuIG9ic2VydmUgY29udmVyZ2VuY2UgYXQgdGhlIGZpbmFsIHJlcGV0aXRpb24pLgpCdXQgaXQgaXMgYWxzbyBxdWl0ZSBjbGVhciB0aGF0IGNvbnZlcmdlbmNlIHJhcmVseSBoYXBwZW5zIGJlZm9yZSB0aGUgZmluYWwgcmVwZXRpdGlvbiwgd2hpY2ggcmVxdWlyZXMgbW9yZSB0aGFuIHR3byBzbWFsbCBjaGFuZ2VzIGluIGEgcm93LgpQZWFyc29uJ3MgcjogYHIgY29yKGNvbnZlcmdlbmNlJGNvbnZlcmdlbmNlX3BvaW50LCBjb252ZXJnZW5jZSR0b3RhbF9yZXBzLCB1c2UgPSAiY29tcGxldGUub2JzIilgLgpgYGB7cn0KZ2dwbG90KGZpbHRlcihjb252ZXJnZW5jZSwgIWlzLm5hKGNvbnZlcmdlbmNlX3BvaW50KSksIGFlcyh4ID0gdG90YWxfcmVwcywgeSA9IGNvbnZlcmdlbmNlX3BvaW50KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4yKSArCiAgY29vcmRfZXF1YWwoKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKQpgYGAKCgoKRG8gY2hhbmdlcyBpbiBSb0YgYmVjb21lIHNtYWxsZXIgYXMgdGhlIG51bWJlciBvZiByZXBldGl0aW9ucyBpbmNyZWFzZXM/CkFzIHRoZSBwbG90IGJlbG93IHNob3dzOiB5ZXMsIGJ1dCB1cCB0byBhIHBvaW50LiBBZnRlciBhYm91dCA3IHJlcGV0aXRpb25zIHRoZSBzaXplIG9mIHRoZSBjaGFuZ2Ugc3RheXMgbW9yZSBvciBsZXNzIGNvbnN0YW50LgpgYGB7cn0KY29udmVyZ2VuY2VfdHJhY2VzICU+JQogIGdyb3VwX2J5KHJlcGV0aXRpb24pICU+JQogIHN1bW1hcmlzZShtZWFuX2NoYW5nZSA9IG1lYW4oYWJzX2RfYWxwaGEpLAogICAgICAgICAgICBzZV9jaGFuZ2UgPSBwbG90cml4OjpzdGQuZXJyb3IoYWJzX2RfYWxwaGEpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZXBldGl0aW9uLCB5ID0gbWVhbl9jaGFuZ2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPSBtZWFuX2NoYW5nZSAtIHNlX2NoYW5nZSwgeW1heCA9IG1lYW5fY2hhbmdlICsgc2VfY2hhbmdlKSkgKwogIGxhYnMoeCA9ICJSZXBldGl0aW9uIiwKICAgICAgIHkgPSAiTWVhbiBjaGFuZ2UgaW4gUm9GIGVzdGltYXRlIiwKICAgICAgIGNhcHRpb24gPSAiRXJyb3IgYmFycyBzaG93ICsvLSAxIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtZWFuIikKYGBgCgoKIyMjIENvbnZlcmdlbmNlIHBvaW50IH4gZmluYWwgUm9GCgpUaGUgY29udmVyZ2VuY2UgcG9pbnQgc2VlbXMgdG8gZGVwZW5kIG9uIHRoZSBmaW5hbCByYXRlIG9mIGZvcmdldHRpbmc6IHRoZSBoaWdoZXIgdGhlIHJhdGUgb2YgZm9yZ2V0dGluZywgdGhlIGxhdGVyIGNvbnZlcmdlbmNlIGhhcHBlbnMuCkltcG9ydGFudGx5LCB0aGlzIHRyZW5kIHNlZW1zIHRvIGV4aXN0IGluIGFsbCBjb25kaXRpb25zLgpgYGB7cn0KcXVhbnRpbGVzIDwtIHF1YW50aWxlKGZpbHRlcihkaXN0aW5jdChjb252ZXJnZW5jZV90cmFjZXMsIGRhdGFzZXQsIHN1YmplY3QsIGNvbmRpdGlvbiwgZmFjdF9pZCwgY29udmVyZ2VzLCBmaW5hbF9hbHBoYSksIGNvbnZlcmdlcykkZmluYWxfYWxwaGEsCiAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjIpKQoKY29udmVyZ2VuY2VfdHJhY2VzICU+JQogIGZpbHRlcihjb252ZXJnZXMpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoZmluYWxfYWxwaGFfYmluID0gY3V0KGZpbmFsX2FscGhhLCBxdWFudGlsZXMsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpICU+JQogIGdyb3VwX2J5KGRhdGFzZXQsIHN1YmplY3QsIGNvbmRpdGlvbiwgZmFjdF9pZCwgZmluYWxfYWxwaGEpICU+JQogIHNsaWNlKG4oKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmluYWxfYWxwaGFfYmluLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQpKSArCiAgZmFjZXRfZ3JpZCh+IGNvbmRpdGlvbikgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBmaW5hbF9hbHBoYV9iaW4pLCB3aWR0aCA9IDAuMiwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Zpb2xpbihmaWxsID0gTkEpICsKICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJGaW5hbCByYXRlIG9mIGZvcmdldHRpbmcgKGJpbm5lZCBpbiBxdWFudGlsZXMpIiwKICAgICAgIHkgPSAiQ29udmVyZ2VuY2UgcG9pbnQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQogICAKYGBgCgpgYGB7cn0KY29udmVyZ2VuY2VfZmluYWxfcm9mIDwtIGRpc3RpbmN0KGNvbnZlcmdlbmNlX3RyYWNlcywgZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkLCBmaW5hbF9hbHBoYSwgY29udmVyZ2VzLCBjb252ZXJnZW5jZV9wb2ludCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihjb252ZXJnZXMpICU+JQogIG11dGF0ZShkYXRhc2V0ID0gYXMuZmFjdG9yKGRhdGFzZXQpLAogICAgICAgICBzdWJqZWN0ID0gYXMuZmFjdG9yKHN1YmplY3QpLAogICAgICAgICBmYWN0X2lkID0gYXMuZmFjdG9yKGZhY3RfaWQpKQoKbV9jb252X2ZpbmFsX3JvZiA8LSBsbWVyKGNvbnZlcmdlbmNlX3BvaW50IH4gZmluYWxfYWxwaGEgKiBjb25kaXRpb24gICsgKDEgfCBzdWJqZWN0KSArICgxIHwgZmFjdF9pZCksIGRhdGEgPSBjb252ZXJnZW5jZV9maW5hbF9yb2YpCgpzdW1tYXJ5KG1fY29udl9maW5hbF9yb2YpCmBgYAoKVGhlIGxtZXIgY29uZmlybXMgYSBsYXJnZSwgcG9zaXRpdmUgbWFpbiBlZmZlY3Qgb2YgZmluYWwgcmF0ZSBvZiBmb3JnZXR0aW5nIG9uIGNvbnZlcmdlbmNlIHBvaW50LCBidXQgbm8gZWZmZWN0IG9mLCBvciBpbnRlcmFjdGlvbiB3aXRoLCBjb25kaXRpb24uCgpUaGlzIGltcGxpZXMgdGhhdCwgcmVnYXJkbGVzcyBvZiB0aGUgY29uZGl0aW9uLCBjb252ZXJnZW5jZSB3aWxsIGhhcHBlbiBzb29uZXIgaWYgdGhlIGZpbmFsIGVzdGltYXRlIGlzIGxvd2VyLgoKCkNvbXBhcmUgdGhlIGZpbmFsIHJhdGUgb2YgZm9yZ2V0dGluZyBkaXN0cmlidXRpb24gb2YgZXN0aW1hdGVzIHRoYXQgZGlkIGNvbnZlcmdlIHRvIHRob3NlIHRoYXQgZGlkIG5vdC4KYGBge3J9CmNvbnZlcmdlbmNlX3RyYWNlcyAlPiUKICBkaXN0aW5jdChkYXRhc2V0LCBzdWJqZWN0LCBjb25kaXRpb24sIGZhY3RfaWQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbnZlcmdlcywgeSA9IGZpbmFsX2FscGhhKSkgKwogIGZhY2V0X2dyaWQofiBjb25kaXRpb24pICsKICBnZW9tX2ppdHRlcihhZXMoY29sb3VyID0gY29udmVyZ2VzKSwgd2lkdGggPSAwLjIsIGFscGhhID0gMC4yKSArCiAgZ2VvbV92aW9saW4oZmlsbCA9IE5BKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpCgpgYGAKCgpgYGB7cn0KY29udiA8LSBjb252ZXJnZW5jZV90cmFjZXMgJT4lCiAgZGlzdGluY3QoZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkLCAua2VlcF9hbGwgPSBUUlVFKQoKbV9jb252X2ZpbmFsIDwtIGxtZXIoY29udmVyZ2VzIH4gZmluYWxfYWxwaGEgKiBjb25kaXRpb24gKyAoMSB8IHN1YmplY3QpICsgKDEgfCBmYWN0X2lkKSwgZGF0YSA9IGNvbnYpCnN1bW1hcnkobV9jb252X2ZpbmFsKQpgYGAKCgpUYWtlYXdheToKCi0gRXN0aW1hdGVzIHRoYXQgZG8gY29udmVyZ2UgdGVuZCB0byBoYXZlIGEgaGlnaGVyIGZpbmFsIHZhbHVlIHRoYW4gZXN0aW1hdGVzIHRoYXQgZG9uJ3QgKHdoaWNoIG1ha2VzIHNvbWUgc2Vuc2UsIHNpbmNlIHRoZXJlIGFyZSBtb3JlIG9wcG9ydHVuaXRpZXMgZm9yIGFkanVzdG1lbnQvc3RhYmlsaXNpbmcpLgotIEZvciBlc3RpbWF0ZXMgdGhhdCBjb252ZXJnZSwgY29udmVyZ2VuY2UgaGFwcGVucyBzb29uZXIgd2hlbiB0aGUgZmluYWwgdmFsdWUgaXMgbG93ZXIuCi0gVHJhZGVvZmY6IGxvd2VyIGZpbmFsIHZhbHVlIG1lYW5zIGxlc3MgbGlrZWx5IHRvIGNvbnZlcmdlLCBidXQgd2hlbiBjb252ZXJnZW5jZSBkb2VzIGhhcHBlbiwgaXQncyBmYXN0ZXIuIAoKCiMjIyBDb252ZXJnZW5jZSBwb2ludCB+IGluaXRpYWwgUm9GCgpJcyB0aGUgYWJvdmUgYWxzbyB0cnVlIGZvciB0aGUgaW5pdGlhbCByYXRlIG9mIGZvcmdldHRpbmcgZXN0aW1hdGU/Ck1heWJlLCBqdWRnaW5nIGJ5IHRoZSBwbG90IGJlbG93IChub3RlIHRoYXQgdGhlIHF1YW50aWxlcyBhcmUgcmVjYWxjdWxhdGVkIGJhc2VkIG9uIGluaXRpYWwgYWxwaGEgdmFsdWVzKS4KYGBge3J9CnF1YW50aWxlcyA8LSBxdWFudGlsZShmaWx0ZXIoY29udmVyZ2VuY2VfdHJhY2VzLCBjb252ZXJnZXMsIHJlcGV0aXRpb24gPT0gMSkkYWxwaGEsCiAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjIpKQoKY29udmVyZ2VuY2VfdHJhY2VzICU+JQogIGZpbHRlcihjb252ZXJnZXMsIHJlcGV0aXRpb24gPT0gMSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShhbHBoYV9iaW4gPSBjdXQoYWxwaGEsIHF1YW50aWxlcywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYWxwaGFfYmluLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQpKSArCiAgZmFjZXRfZ3JpZCh+IGNvbmRpdGlvbikgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBhbHBoYV9iaW4pLCB3aWR0aCA9IDAuMiwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Zpb2xpbihmaWxsID0gTkEpICsKICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJJbml0aWFsIHJhdGUgb2YgZm9yZ2V0dGluZyAoYmlubmVkIGluIHF1YW50aWxlcykiLAogICAgICAgeSA9ICJDb252ZXJnZW5jZSBwb2ludCIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCgpgYGAKCmBgYHtyfQpjb252ZXJnZW5jZV9pbml0X3JvZiA8LSBjb252ZXJnZW5jZV90cmFjZXMgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihjb252ZXJnZXMsIHJlcGV0aXRpb24gPT0gMSkgJT4lCiAgbXV0YXRlKGRhdGFzZXQgPSBhcy5mYWN0b3IoZGF0YXNldCksCiAgICAgICAgIHN1YmplY3QgPSBhcy5mYWN0b3Ioc3ViamVjdCksCiAgICAgICAgIGZhY3RfaWQgPSBhcy5mYWN0b3IoZmFjdF9pZCkpCgptX2NvbnZfaW5pdF9yb2YgPC0gbG1lcihjb252ZXJnZW5jZV9wb2ludCB+IGFscGhhICogY29uZGl0aW9uICArICgxIHwgc3ViamVjdCkgKyAoMSB8IGZhY3RfaWQpLCBkYXRhID0gY29udmVyZ2VuY2VfaW5pdF9yb2YpCgpzdW1tYXJ5KG1fY29udl9pbml0X3JvZikKYGBgCgoKVGhlIGxtZXIgY29uZmlybXMgdGhhdCB0aGUgY29udmVyZ2VuY2UgcG9pbnQgaW5jcmVhc2VzIGFzIGEgZnVuY3Rpb24gb2YgdGhlIGluaXRpYWwgcmF0ZSBvZiBmb3JnZXR0aW5nIChgYWxwaGFgKSBpbiBhbGwgY29uZGl0aW9ucywgZXZlbiBhY2NvdW50aW5nIGZvciB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB0aGUgaW5pdGlhbCBhbHBoYSBhbmQgdGhlIGNvbmRpdGlvbi4KCkNvbnZlcmdlbmNlIHNlZW1zIHRvIGJlIGFib3V0IGVxdWFsbHkgbGlrZWx5IGZvciBsb3cgYW5kIGhpZ2ggaW5pdGlhbCB2YWx1ZXMuCmBgYHtyfQpjb252ZXJnZW5jZV90cmFjZXMgJT4lCiAgZGlzdGluY3QoZGF0YXNldCwgc3ViamVjdCwgY29uZGl0aW9uLCBmYWN0X2lkLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb252ZXJnZXMsIHkgPSBhbHBoYSkpICsKICBmYWNldF9ncmlkKH4gY29uZGl0aW9uKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91ciA9IGNvbnZlcmdlcyksIHdpZHRoID0gMC4yLCBhbHBoYSA9IDAuMikgKwogIGdlb21fdmlvbGluKGZpbGwgPSBOQSkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKQoKYGBgCgoKVGhlIGxhY2sgb2YgYW4gZWZmZWN0IGlzIGNvbmZpcm1lZCBieSBhbiBsbWVyOgpgYGB7cn0KbV9jb252X2luaXQgPC0gbG1lcihjb252ZXJnZXMgfiBhbHBoYSAqIGNvbmRpdGlvbiArICgxIHwgc3ViamVjdCkgKyAoMSB8IGZhY3RfaWQpLCBkYXRhID0gY29udikKc3VtbWFyeShtX2NvbnZfaW5pdCkKYGBgCgoKCldlIGNhbiBzdXJtaXNlIHRoYXQgdGhlIGNvbnZlcmdlbmNlIHBvaW50IGlzIHNlbnNpdGl2ZSB0byBib3RoIHRoZSBpbml0aWFsIGVzdGltYXRlIGFuZCB0aGUgZmluYWwgZXN0aW1hdGUuCldoZW4gY29uZGl0aW9ucyBoYXZlIHVuZXF1YWwgaW5pdGlhbCBlc3RpbWF0ZXMsIGFzIGlzIHRoZSBjYXNlIGhlcmUgKHRoZSBpbml0aWFsIGVzdGltYXRlcyBpbiB0aGUgYWRhcHRpdmUgY29uZGl0aW9ucyB0ZW5kIHRvIGJlIGhpZ2hlciB0aGFuIHRob3NlIGluIHRoZSBkZWZhdWx0IGNvbmRpdGlvbiksIHRoaXMgY29uZm91bmRzIHRoZSByZXN1bHRzLgoKSXQgaXMgY2xlYXIgdGhhdCB0aGUgb3BlcmF0aW9uYWxpc2F0aW9uIG9mIHRoZSBjb252ZXJnZW5jZSBwb2ludCBpcyBwcm9ibGVtYXRpYzogb25lIHdvdWxkIGNvbWUgdG8gdGhlIGNvbmNsdXNpb24gdGhhdCBhIGxvd2VyIGluaXRpYWwgZXN0aW1hdGUgaXMgYWx3YXlzIGJldHRlciwgc2ltcGx5IGJlY2F1c2UgdGhhdCBpcyByZXdhcmRlZCBpbiB0aGUgY3VycmVudCBhbmFseXNpcy4KV2UgbWF5IHdhbnQgdG8gdGhpbmsgYWJvdXQgYSBtZWFzdXJlIHRoYXQgaXMgcm9idXN0IHRvIHRoaXMuCgpJZGVhczoKCi0gU3BsaXQgdGhlIGRhdGEgb24gaW5pdGlhbCBlc3RpbWF0ZSAobG93ZXIgdGhhbiAwLjMgb3IgaGlnaGVyIHRoYW4gMC4zKSBhbmQgc2VlIGlmIGNvbnZlcmdlbmNlIGltcHJvdmVzIGJlbG93IDAuMy4KLSAuLi4KCgoKVGhlIGxvd2VyIGNvbnZlcmdlbmNlIHN1Y2Nlc3MgaW4gYWRhcHRpdmUgZGlzdHJpYnV0aW9ucyBtYXkgYmUgYmVjYXVzZSB0aGUgZmluYWwgYWxwaGEgbWlnaHQgYmUgaGlnaGVyIGluIHRoZXNlIGNvbmRpdGlvbnMuClRoZXJlIHNlZW1zIHRvIGJlIGEgc2xpZ2h0bHkgbG93ZXIgZmluYWwgcmF0ZSBvZiBmb3JnZXR0aW5nIGluIHRoZSBkZWZhdWx0IGNvbmRpdGlvbiBjb21wYXJlZCB0byB0aGUgYWRhcHRpdmUgY29uZGl0aW9ucywgYnV0IHRoaXMgbW9zdGx5IGRpc2FwcGVhcnMgaW4gcGFpcndpc2UgY29tcGFyaXNvbnMgKGFsc28gd2hlbiB3ZSBvbmx5IGxvb2sgYXQgY29udmVyZ2VkIGVzdGltYXRlcyksIGFuZCBpcyBhbHNvIG5vdCBzdXBwb3J0ZWQgYnkgYSBCYXllc2lhbiBsbUJGLCB3aGljaCBzbGlnaHRseSBwcmVmZXJzIGEgbW9kZWwgd2l0aG91dCBjb25kaXRpb24uCmBgYHtyfQpwX2ZpbmFsX3JvZiA8LSBnZ3Bsb3QoY29udiwgYWVzKHggPSBjb25kaXRpb24sIHkgPSBmaW5hbF9hbHBoYSkpICsKICBnZW9tX3Zpb2xpbigpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMSwgYWVzKGNvbG91ciA9IGNvbmRpdGlvbikpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCB3aWR0aCA9IDAuNSkgKwogIGxhYnMoeCA9ICJDb25kaXRpb24iLAogICAgICAgeSA9ICJGaW5hbCByYXRlIG9mIGZvcmdldHRpbmciKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKQoKdGlreihmaWxlID0gIi4uL291dHB1dC9maW5hbF9yb2ZfYnlfY29uZC50ZXgiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDIuNSkKcF9maW5hbF9yb2YgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiRGVmYXVsdCIsICJEb21haW4iLCAiRmFjdCIsICJMZWFybmVyIiwgIkZhY3QgXFwmIExlYXJuZXIiKSkgKwogIHRoZW1lX2xpZ2h0KCkKZGV2Lm9mZigpCgpwX2ZpbmFsX3JvZgpgYGAKCmBgYHtyfQpzdW1tYXJ5KG1fZmluYWxfcm9mX2NvbmQgPC0gbG1lcihmaW5hbF9hbHBoYSB+IGNvbmRpdGlvbiArICgxIHwgc3ViamVjdCkgKyAoMSB8IGZhY3RfaWQpLCBkYXRhID0gY29udikpCgpiZl9maW5hbF9yb2ZfY29uZCA8LSBsbUJGKAogIGZvcm11bGEgPSBmaW5hbF9hbHBoYSB+IGNvbmRpdGlvbiArIHN1YmplY3QgKyBmYWN0X2lkLAogIHdoaWNoUmFuZG9tID0gYygic3ViamVjdCIsICJmYWN0X2lkIiksCiAgZGF0YSA9IG11dGF0ZSh1bmdyb3VwKGNvbnYpLCBzdWJqZWN0ID0gYXMuZmFjdG9yKHN1YmplY3QpLCBmYWN0X2lkID0gYXMuZmFjdG9yKGZhY3RfaWQpKSwKICBwcm9ncmVzcyA9IEZBTFNFCikKICAKYmZfZmluYWxfcm9mX25vY29uZCA8LSBsbUJGKAogIGZvcm11bGEgPSBmaW5hbF9hbHBoYSB+IHN1YmplY3QgKyBmYWN0X2lkLAogIHdoaWNoUmFuZG9tID0gYygic3ViamVjdCIsICJmYWN0X2lkIiksCiAgZGF0YSA9IG11dGF0ZSh1bmdyb3VwKGNvbnYpLCBzdWJqZWN0ID0gYXMuZmFjdG9yKHN1YmplY3QpLCBmYWN0X2lkID0gYXMuZmFjdG9yKGZhY3RfaWQpKSwKICBwcm9ncmVzcyA9IEZBTFNFCikKICAKYmZfZmluYWxfcm9mX2NvbmQgLyBiZl9maW5hbF9yb2Zfbm9jb25kIAoKYGBgCgpUYW5nZW50aWFsOiBob3cgc2ltaWxhciBhcmUgZmluYWwgcmF0ZXMgb2YgZm9yZ2V0dGluZyBmb3Igc3BlY2lmaWMgZmFjdHMgYmV0d2VlbiBjb25kaXRpb25zPwpMb29rcyBmYWlybHkgY29uc2lzdGVudCBhY3Jvc3MgY29uZGl0aW9uczoKYGBge3J9CmNvbnYgJT4lCiAgZ3JvdXBfYnkoY29uZGl0aW9uLCBmYWN0X2lkKSAlPiUKICBzdW1tYXJpc2UoYWxwaGEgPSBtZWFuKGZpbmFsX2FscGhhKSwKICAgICAgICAgICAgbiA9IG4oKSkgJT4lCiAgIyBtdXRhdGUocmFuayA9IGRlbnNlX3JhbmsoZGVzYyhhbHBoYSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBhbHBoYSwgZ3JvdXAgPSBmYWN0X2lkLCBjb2xvdXIgPSBhcy5mYWN0b3IoZmFjdF9pZCkpKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBuKSkgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkKYGBgCgoKLS0tCgpNYWtlIGRpYWdub3N0aWMgcGxvdHM6CgoKYGBge3J9CmxpYnJhcnkocGF0Y2h3b3JrKQoKIyBDaGFuZ2UgaW4gcmF0ZSBvZiBmb3JnZXR0aW5nIHdpdGggZWFjaCByZXBldGl0aW9uCnBfcm9mX2NoYW5nZSA8LSBjb252ZXJnZW5jZV90cmFjZXMgJT4lCiAgZ3JvdXBfYnkocmVwZXRpdGlvbikgJT4lCiAgc3VtbWFyaXNlKG1lYW5fY2hhbmdlID0gbWVhbihhYnNfZF9hbHBoYSksCiAgICAgICAgICAgIHNlX2NoYW5nZSA9IHBsb3RyaXg6OnN0ZC5lcnJvcihhYnNfZF9hbHBoYSkpICU+JQogIGdncGxvdChhZXMoeCA9IHJlcGV0aXRpb24sIHkgPSBtZWFuX2NoYW5nZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49IG1lYW5fY2hhbmdlIC0gc2VfY2hhbmdlLCB5bWF4ID0gbWVhbl9jaGFuZ2UgKyBzZV9jaGFuZ2UpLCBuYS5ybSA9IFQpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjAwNDk2LCBjb2xvdXIgPSAicmVkIiwgbHR5ID0gMikgKwogIGxhYnMoeCA9ICJSZXBldGl0aW9uIiwKICAgICAgIHkgPSAiQWJzb2x1dGUgcmF0ZSBvZiBmb3JnZXR0aW5nIGNoYW5nZSIpCgogIAojIENvbnZlcmdlbmNlIHBvaW50IH4gdG90YWwgbnVtYmVyIG9mIHJlcGV0aXRpb25zCnBfY29udl9yZXBzIDwtIGdncGxvdChmaWx0ZXIoY29udmVyZ2VuY2UsICFpcy5uYShjb252ZXJnZW5jZV9wb2ludCkpLCBhZXMoeCA9IHRvdGFsX3JlcHMsIHkgPSBjb252ZXJnZW5jZV9wb2ludCkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMikgKwogIGNvb3JkX2VxdWFsKCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGxhYnMoeCA9ICJUb3RhbCByZXBldGl0aW9ucyIsCiAgICAgIHkgPSAiQ29udmVyZ2VuY2UgcG9pbnQiKQoKCnBfY29udl9wb2ludF9pbml0aWFsIDwtIGdncGxvdChjb252LCBhZXMoeCA9IGFscGhhLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQsIGNvbG91ciA9IGNvbmRpdGlvbiwgZmlsbCA9IGNvbmRpdGlvbikpICsKICBmYWNldF9ncmlkKH4gZGF0YXNldCkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEsIG5hLnJtID0gVCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVCwgbmEucm0gPSBULCBzaXplID0gLjc1KSArCiAgIyBnZW9tX3J1ZyhhZXMoeSA9IE5VTEwpLCBzaWRlcyA9ICJiIikgKwogIGxhYnMoeCA9ICJJbml0aWFsIHJhdGUgb2YgZm9yZ2V0dGluZyIsCiAgICAgICAgIHkgPSAiQ29udmVyZ2VuY2UgcG9pbnQiKQoKcF9jb252X3BvaW50X2ZpbmFsIDwtIGdncGxvdChjb252LCBhZXMoeCA9IGZpbmFsX2FscGhhLCB5ID0gY29udmVyZ2VuY2VfcG9pbnQsIGNvbG91ciA9IGNvbmRpdGlvbiwgZmlsbCA9IGNvbmRpdGlvbikpICsKICBmYWNldF9ncmlkKH4gZGF0YXNldCkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEsIG5hLnJtID0gVCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVCwgbmEucm0gPSBULCBzaXplID0gLjc1KSArCiAgIyBnZW9tX3J1ZyhhZXMoeSA9IE5VTEwpLCBzaWRlcyA9ICJiIikgKwogIGxhYnMoeCA9ICJGaW5hbCByYXRlIG9mIGZvcmdldHRpbmciLAogICAgICAgICB5ID0gIkNvbnZlcmdlbmNlIHBvaW50IikKCnBfY29udl9wcm9iX2luaXRpYWwgPC0gZ2dwbG90KGNvbnYsIGFlcyh4ID0gYWxwaGEsIHkgPSBhcy5udW1lcmljKGNvbnZlcmdlcyksIGNvbG91ciA9IGNvbmRpdGlvbiwgZmlsbCA9IGNvbmRpdGlvbikpICsKICBmYWNldF9ncmlkKH4gZGF0YXNldCkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEsIG5hLnJtID0gVCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCAgbWV0aG9kLmFyZ3M9bGlzdChmYW1pbHk9ImJpbm9taWFsIiksIHNlID0gVCwgbmEucm0gPSBUKSArCiAgIyBnZW9tX3J1ZyhhZXMoeSA9IE5VTEwpLCBzaWRlcyA9ICJiIikgKwogIHlsaW0oMCwgMSkgKwogIGxhYnMoeCA9ICJJbml0aWFsIHJhdGUgb2YgZm9yZ2V0dGluZyIsCiAgICAgICB5ID0gIkNvbnZlcmdlbmNlIHByb2JhYmlsaXR5IikKCnBfY29udl9wcm9iX2ZpbmFsIDwtIGdncGxvdChjb252LCBhZXMoeCA9IGZpbmFsX2FscGhhLCB5ID0gYXMubnVtZXJpYyhjb252ZXJnZXMpLCBjb2xvdXIgPSBjb25kaXRpb24sIGZpbGwgPSBjb25kaXRpb24pKSArCiAgZmFjZXRfZ3JpZCh+IGRhdGFzZXQpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4xLCBuYS5ybSA9IFQpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgIG1ldGhvZC5hcmdzPWxpc3QoZmFtaWx5PSJiaW5vbWlhbCIpLCBzZSA9IFQsIG5hLnJtID0gVCkgKwogICMgZ2VvbV9ydWcoYWVzKHkgPSBOVUxMKSwgc2lkZXMgPSAiYiIpICsKICB5bGltKDAsIDEpICsKICBsYWJzKHggPSAiRmluYWwgcmF0ZSBvZiBmb3JnZXR0aW5nIiwKICAgICAgIHkgPSAiQ29udmVyZ2VuY2UgcHJvYmFiaWxpdHkiKQoKCnBfcm9mX2NoYW5nZSArIHBfY29udl9yZXBzICsgcF9jb252X3BvaW50X2luaXRpYWwgKyBwX2NvbnZfcHJvYl9pbml0aWFsICsgcF9jb252X3BvaW50X2ZpbmFsICsgcF9jb252X3Byb2JfZmluYWwgKyBwbG90X2xheW91dChuY29sID0gMiwgYnlyb3cgPSBUUlVFKQoKZ2dzYXZlKCIuLi9vdXRwdXQvY29udmVyZ2VuY2VfZGlhZ25vc3RpY3MucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCgpgYGAKClBsb3QgdGhlIFJvRiBjaGFuZ2UgcGxvdCBmb3IgZWFjaCBjb25kaXRpb24gc2VwYXJhdGVseToKYGBge3J9CnBfcm9mX2NoYW5nZV9jb25kIDwtIGNvbnZlcmdlbmNlX3RyYWNlcyAlPiUKICBncm91cF9ieShjb25kaXRpb24sIHJlcGV0aXRpb24pICU+JQogIHN1bW1hcmlzZShtZWFuX2NoYW5nZSA9IG1lYW4oYWJzX2RfYWxwaGEpLAogICAgICAgICAgICBzZV9jaGFuZ2UgPSBwbG90cml4OjpzdGQuZXJyb3IoYWJzX2RfYWxwaGEpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZXBldGl0aW9uLCB5ID0gbWVhbl9jaGFuZ2UsIGNvbG91ciA9IGNvbmRpdGlvbikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPSBtZWFuX2NoYW5nZSAtIHNlX2NoYW5nZSwgeW1heCA9IG1lYW5fY2hhbmdlICsgc2VfY2hhbmdlKSwgbmEucm0gPSBUKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC4wMDQ5NiwgY29sb3VyID0gInJlZCIsIGx0eSA9IDIpICsKICBsYWJzKHggPSAiUmVwZXRpdGlvbiIsCiAgICAgICB5ID0gIkFic29sdXRlIHJhdGUgb2YgZm9yZ2V0dGluZyBjaGFuZ2UiKSArCiAgeGxpbSgwLDEwKSAKCnBfcm9mX2NoYW5nZV9jb25kCmBgYAoKCkFsc28gcGxvdCBlYWNoIGRpYWdub3N0aWMgcGxvdCBzZXBhcmF0ZWx5OgpgYGB7cn0KdGlreihmaWxlID0gIi4uL291dHB1dC9yb2ZfY2hhbmdlLnRleCIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMi41KQpwX3JvZl9jaGFuZ2UgKwogIHRoZW1lX2xpZ2h0KCkKZGV2Lm9mZigpCgp0aWt6KGZpbGUgPSAiLi4vb3V0cHV0L2NvbnZfcmVwcy50ZXgiLCB3aWR0aCA9IDIuNSwgaGVpZ2h0ID0gMi41KQpwX2NvbnZfcmVwcyArCiAgdGhlbWVfbGlnaHQoKQpkZXYub2ZmKCkKCnRpa3ooZmlsZSA9ICIuLi9vdXRwdXQvY29udl9wb2ludF9pbml0aWFsX3JvZi50ZXgiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDIpCnBfY29udl9wb2ludF9pbml0aWFsICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgdGhlbWVfbGlnaHQoKQpkZXYub2ZmKCkKCnRpa3ooZmlsZSA9ICIuLi9vdXRwdXQvY29udl9wb2ludF9maW5hbF9yb2YudGV4Iiwgd2lkdGggPSA0LCBoZWlnaHQgPSAyKQpwX2NvbnZfcG9pbnRfZmluYWwgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoIkRlZmF1bHQiLCAiRG9tYWluIiwgIkZhY3QiLCAiTGVhcm5lciIsICJGYWN0IFxcJiBMZWFybmVyIikpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKGxhYmVscyA9IGMoIkRlZmF1bHQiLCAiRG9tYWluIiwgIkZhY3QiLCAiTGVhcm5lciIsICJGYWN0IFxcJiBMZWFybmVyIikpICsKICB0aGVtZV9saWdodCgpCmRldi5vZmYoKQoKdGlreihmaWxlID0gIi4uL291dHB1dC9jb252X3Byb2JfaW5pdGlhbF9yb2YudGV4Iiwgd2lkdGggPSA0LCBoZWlnaHQgPSAyKQpwX2NvbnZfcHJvYl9pbml0aWFsICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgdGhlbWVfbGlnaHQoKQpkZXYub2ZmKCkKCnRpa3ooZmlsZSA9ICIuLi9vdXRwdXQvY29udl9wcm9iX2ZpbmFsX3JvZi50ZXgiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDIpCnBfY29udl9wcm9iX2ZpbmFsICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJEZWZhdWx0IiwgIkRvbWFpbiIsICJGYWN0IiwgIkxlYXJuZXIiLCAiRmFjdCBcXCYgTGVhcm5lciIpKSArCiAgdGhlbWVfbGlnaHQoKQpkZXYub2ZmKCkKCmBgYAoKCgoKLS0tCgojIFNlc3Npb24gaW5mb3JtYXRpb24KYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=